def p_type_id(p): 'type : ID' global current_type baseclass = ast.lookup(ast.classtable, p[1]) if (baseclass == None): signal_error('Class {0} does not exist!'.format(p[1]), p.lineno(1)) p[0] = current_type = ast.Type(baseclass.name)
def p_extends_id(p): 'extends : EXTENDS ID ' cid = ast.lookup(ast.classtable, p[2]) if (not cid): signal_error('Class {0} does not exist!'.format(p[2]), p.lineno(2)) p[0] = cid pass
def p_primary_newobj(p): 'primary : NEW ID LPAREN args_opt RPAREN' cname = p[2] c = ast.lookup(ast.classtable, cname) if (c != None): p[0] = ast.NewObjectExpr(c, p[4], p.lineno(1)) else: signal_error('Class "{0}" in "new" not defined (yet?)'.format(cname), p.lineno(2))
def p_class_decl_head(p): 'class_decl_head : CLASS ID extends' global current_class, current_context cid = p[2] sc = p[3] c = ast.lookup(ast.classtable, cid) if (c != None): signal_error('Class {0} already exists!'.format(cid), p.lineno(2)) else: c = ast.Class(cid, sc) ast.addtotable(ast.classtable, cid, c) current_class = c current_context = 'class' pass
def p_field_access_id(p): 'field_access : ID' vname = p[1] v = current_vartable.find_in_scope(vname) if (v != None): # local variable in current scope p[0] = ast.VarExpr(v, p.lineno(1)) else: c = ast.lookup(ast.classtable, vname) if (c != None): # there is a class with this name p[0] = ast.ClassReferenceExpr(c, p.lineno(1)) else: # reference to a non-local var; assume field p[0] = ast.FieldAccessExpr(ast.ThisExpr(p.lineno(1)), vname, p.lineno(1))
def isSubtype(type1, type2): #if types are same if str(type1) == str(type2): return True # predefined int float relationship elif str(type1) == 'int' and str(type2) == 'float': return True # checking subtype relation between two class objects if # type2 is superclass of type1 elif "user" in str(type1) and "user" in str(type2): A = str(type1)[5:-1] B = str(type2)[5:-1] classA = ast.lookup(ast.classtable, A) if (classA != None and classA.superclass != None and classA.superclass.name == B): return True #null is subtype of class object elif str(type1) == 'null' and "user" in str(type2): return True #if a is subclass of b then class-leteral of a is subtype of class-literal of b elif 'class-literal' in str(type1) and 'class-literal' in str(type2): A = str(type1)[14:-1] B = str(type2)[14:-1] classA = ast.lookup(ast.classtable, A) if (classA != None and classA.superclass.name == B): return True #recursively check if array types are same by stripping of the array( part elif 'array(' in str(type1) and 'array(' in str(type2): A = str(type1)[6:-1] B = str(type2)[6:-1] if (isSubtype(A, B)): return True elif str(type1) == 'null' and 'array(' in str(type2): return True return False
def expr_error(expr): '''Return whether or not the expression has a type or resolution error. Also ensures that the type of the expression is calculated. Call this first if you need to use the type of an expression.''' expr.type = None if isinstance(expr, ast.ConstantExpr): if expr.kind == 'int' or expr.kind == 'float' or expr.kind == 'string': expr.type = ast.Type(expr.kind) elif expr.kind == 'Null': expr.type = ast.Type('null') elif expr.kind == 'True' or expr.kind == 'False': expr.type = ast.Type('boolean') else: expr.type = ast.Type('error') signal_error('Unknown type {}'.format(expr.type), expr.lines) elif isinstance(expr, ast.VarExpr): expr.type = expr.var.type elif isinstance(expr, ast.UnaryExpr): arg = expr.arg arg_err = expr_error(arg) if arg_err: expr.type = ast.Type('error') elif expr.uop == 'uminus': if arg.type.is_numeric(): expr.type = arg.type else: signal_error( 'Expecting an integer or float argument to unary ' 'minus. Found {}'.format(arg.type), expr.lines) expr.type = ast.Type('error') elif expr.uop == 'neg': if arg.type == ast.Type('boolean'): expr.type = arg.type else: signal_error( 'Expecting a boolean argument to ! (negation). ' 'Found {}'.format(arg.type), expr.lines) expr.type = ast.Type('error') elif isinstance(expr, ast.BinaryExpr): op_names = { 'add': '+', 'sub': '-', 'mul': '*', 'div': '/', 'and': '&&', 'or': '||', 'eq': '==', 'neq': '!=', 'lt': '<', 'leq': '<=', 'gt': '>', 'geq': '>=' } bop = expr.bop arg1 = expr.arg1 arg2 = expr.arg2 arg1_err = expr_error(arg1) arg2_err = expr_error(arg2) if arg1_err or arg2_err: expr.type = ast.Type('error') elif bop == 'add' or bop == 'sub' or bop == 'mul' or bop == 'div': type1 = arg1.type type2 = arg2.type if type1.is_numeric() and type2.is_numeric(): if type1 == ast.Type('float') or type2 == ast.Type('float'): expr.type = ast.Type('float') else: expr.type = ast.Type('int') else: signal_error( 'Expecting float or integer arguments for the ' 'operator "{}". Found {} on the left and {} on ' 'the right.'.format(op_names[bop], type1, type2), expr.lines) expr.type = ast.Type('error') elif bop == 'and' or bop == 'or': type1 = arg1.type type2 = arg2.type if type1 == ast.Type('boolean') and type2 == ast.Type('boolean'): expr.type = ast.Type('boolean') else: signal_error( 'Expecting boolean arguments for the operator ' '"{}". Found {} on the left and {} on the ' 'right.'.format(op_names[bop], type1, type2), expr.lines) elif bop == 'lt' or bop == 'leq' or bop == 'gt' or bop == 'geq': type1 = arg1.type type2 = arg2.type if type1.is_numeric() and type2.is_numeric(): expr.type = ast.Type('boolean') else: signal_error( 'Expecting int or float arguments for the' 'operator "{}". Found {} on the left and {} on ' 'the right.'.format(op_names[bop], type1, type2), expr.lines) expr.type = ast.Type('error') elif bop == 'eq' or bop == 'neq': type1 = arg1.type type2 = arg2.type if type1.subtype_of(type2) or type2.subtype_of(type1): expr.type = ast.Type('boolean') else: signal_error( 'Expecting compatible arguments for the operator ' '"{}". One argument must be the subtype of the ' 'other. Found {} on the left and {} on the ' 'right.'.format(op_names[bop], type1, type2), expr.lines) expr.type = ast.Type('error') elif isinstance(expr, ast.FieldAccessExpr): err = expr_error(expr.base) if err: expr.type = ast.Type('error') return True cls = ast.lookup(ast.classtable, expr.base.type.typename) cur_cls = cls while cur_cls is not None: field = ast.lookup(cur_cls.fields, expr.fname) if field is not None: # Ensure it's not static, and not accessed by Class.foo, and it's accessable if (expr.base.type.kind == 'class') and (field.storage != 'static') \ and ((field.visibility != 'private') or (expr.base.type.typename == current_class.name)): break # Ensure it's static, and accessed by Class.foo, and it's accessable if (expr.base.type.kind == 'class-literal') and (field.storage == 'static') \ and ((field.visibility != 'private') or (expr.base.type.typename == current_class.name)): break field = None cur_cls = cur_cls.superclass if field is None: signal_error('Could not resolve field {}'.format(expr.fname), expr.lines) expr.type = ast.Type('error') return True expr.type = ast.Type(field.type) elif isinstance(expr, ast.ThisExpr): expr.type = ast.Type(current_class.name) elif isinstance(expr, ast.MethodInvocationExpr): err = expr_error(expr.base) if err: expr.type = ast.Type('error') return True cls = ast.lookup(ast.classtable, expr.base.type.typename) method = None cur_cls = cls while cur_cls is not None: for i in cur_cls.methods: if expr.mname == i.name: method = i break if method is not None: if (expr.base.type.kind == 'class') and (method.storage != 'static') \ and ((method.visibility != 'private') or (expr.base.type.typename == current_class.name)): break if (expr.base.type.kind == 'class-literal') and (method.storage == 'static') \ and ((method.visibility != 'private') or (expr.base.type.typename == current_class.name)): break method = None cur_cls = cur_cls.superclass if method is None: expr.type = ast.Type('error') signal_error('Could not resolve method \'{}\''.format(expr.mname), expr.lines) return True method_params = method.vars.vars[0].values() # Ensure number of params match if len(method_params) != len(expr.args): expr.type = ast.Type('error') signal_error( 'Method \'{}\' expects {} args, received {}'.format( method.name, len(method_params), len(expr.args)), expr.lines) return True for i in range(0, len(method_params)): arg_err = expr_error(expr.args[i]) if arg_err: expr.type = ast.Type('error') return True nth_param = ast.Type(method_params[i].type) nth_arg = ast.Type(expr.args[i].type) if not nth_arg.subtype_of(nth_param): expr.type = ast.Type('error') signal_error( 'Method argument number {0} is not a subtype ' 'of construtor parameter number {0}. Expects \'{1}\', received \'{2}\'.' .format(i + 1, nth_param.typename, nth_arg.typename), expr.lines) return True expr.type = ast.Type(method.rtype) elif isinstance(expr, ast.AssignExpr): lhs_err = expr_error(expr.lhs) rhs_err = expr_error(expr.rhs) if lhs_err or rhs_err: expr.type = ast.Type('error') return True lhs = ast.Type(expr.lhs.type) rhs = ast.Type(expr.rhs.type) if not rhs.subtype_of(lhs): expr.type = ast.Type('error') signal_error('{} not a subtype of {}'.format(rhs, lhs), expr.lines) else: expr.type = ast.Type(rhs) elif isinstance(expr, ast.NewObjectExpr): cls = ast.lookup(ast.classtable, expr.classref.name) expr.constr_id = None # After ensuring the # of args match, if there's no constructor, allow it if len(cls.constructors) == 0 and len(expr.args) == 0: expr.type = ast.Type(expr.classref.name) return False if cls.constructors[0].visibility == 'private': expr.type = ast.Type('error') signal_error( 'Constructor for class {} is private'.format(cls.name), expr.lines) return True # Ensure number of args match if len(cls.constructors[0].vars.vars[0]) != len(expr.args): expr.type = ast.Type('error') signal_error( 'Constructor for class {} expects {} arguments, received {}'. format(cls.name, len(cls.constructors[0].vars.vars[0]), len(expr.args)), expr.lines) return True constr_params = cls.constructors[0].vars.vars[0].values() # Ensure each arg is a subtype of each param for i in range(0, len(constr_params)): arg_err = expr_error(expr.args[i]) if arg_err: expr.type = ast.Type('error') return True nth_param = ast.Type(constr_params[i].type) nth_arg = ast.Type(expr.args[i].type) if not nth_arg.subtype_of(nth_param): expr.type = ast.Type('error') signal_error( 'Constructor argument number {0} is not a subtype ' 'of construtor parameter number {0}. Expects \'{1}\', received \'{2}\'.' .format(i + 1, nth_param.typename, nth_arg.typename), expr.lines) return True expr.constr_id = cls.constructors[0].id expr.type = ast.Type(expr.classref.name) elif isinstance(expr, ast.ClassReferenceExpr): expr.type = ast.Type(expr.classref.name, None, True) elif isinstance(expr, ast.SuperExpr): if current_class.superclass is None: signal_error( 'Class {} has no superclass.'.format(current_class.name), expr.lines) expr.type = ast.Type('error') else: expr.type = ast.Type(current_class.superclass.name) elif isinstance(expr, ast.AutoExpr): err = expr_error(expr.arg) if err: expr.type = ast.Type('error') return True if expr.arg.type.is_numeric(): expr.type = ast.Type(expr.arg.type) else: expr.type = ast.Type('error') signal_error( 'Auto expression must be int or float; received {}'.format( expr.arg.type), expr.lines) elif isinstance(expr, ast.ArrayAccessExpr) or isinstance( expr, ast.NewArrayExpr): expr.type = ast.Type('error') signal_error("Array expression found, this is not supported!", expr.lines) return expr.type.is_error()
def gen_code(stmt): global current_loop_continue_label, current_enter_then_label, current_break_out_else_label # stmt.end_reg is the destination register for each expression stmt.end_reg = None push_labels() if isinstance(stmt, ast.BlockStmt): for stmt_line in stmt.stmtlist: gen_code(stmt_line) elif isinstance(stmt, ast.ExprStmt): gen_code(stmt.expr) elif isinstance(stmt, ast.AssignExpr): gen_code(stmt.rhs) gen_code(stmt.lhs) if stmt.lhs.type == ast.Type('float') and stmt.rhs.type == ast.Type('int'): conv = absmc.ConvertInstr('itof', stmt.rhs.end_reg) stmt.rhs.end_reg = conv.dst if not isinstance(stmt.lhs, ast.FieldAccessExpr): absmc.MoveInstr('move', stmt.lhs.end_reg, stmt.rhs.end_reg) else: absmc.HeapInstr('hstore', stmt.lhs.base.end_reg, stmt.lhs.offset_reg, stmt.rhs.end_reg) elif isinstance(stmt, ast.VarExpr): stmt.end_reg = stmt.var.reg elif isinstance(stmt, ast.ConstantExpr): reg = absmc.Register() if stmt.kind == 'int': absmc.MoveInstr('move_immed_i', reg, stmt.int, True) elif stmt.kind == 'float': absmc.MoveInstr('move_immed_f', reg, stmt.float, True) elif stmt.kind == 'string': pass elif stmt.kind == 'True': absmc.MoveInstr('move_immed_i', reg, 1, True) elif stmt.kind == 'False': absmc.MoveInstr('move_immed_i', reg, 0, True) elif stmt.kind == 'Null': absmc.MoveInstr('move_immed_i', reg, 'Null', True) stmt.end_reg = reg elif isinstance(stmt, ast.BinaryExpr): if stmt.bop not in ['and', 'or']: gen_code(stmt.arg1) gen_code(stmt.arg2) reg = absmc.Register() flt = ast.Type('float') intg = ast.Type('int') if stmt.arg1.type == flt or stmt.arg2.type == flt: expr_type = 'f' else: expr_type = 'i' if stmt.arg1.type == intg and stmt.arg2.type == flt: conv = absmc.Convert('itof', stmt.arg1.end_reg) stmt.arg1.end_reg = conv.dst elif stmt.arg1.type == flt and stmt.arg2.type == intg: conv = absmc.Convert('itof', stmt.arg2.end_reg) stmt.arg2.end_reg = conv.dst if stmt.bop in ['add', 'sub', 'mul', 'div', 'gt', 'geq', 'lt', 'leq']: absmc.ArithInstr(stmt.bop, reg, stmt.arg1.end_reg, stmt.arg2.end_reg, expr_type) elif stmt.bop == 'eq' or stmt.bop == 'neq': absmc.ArithInstr('sub', reg, stmt.arg1.end_reg, stmt.arg2.end_reg, expr_type) if stmt.bop == 'eq': # check if r2 == r3 # 1. perform sub r1, r2, r3 (done above) # 2. branch to set_one if r1 is zero # 3. else, fall through and set r1 to zero # 4. jump out so we don't set r1 to one by accident ieq_set = absmc.BranchLabel(stmt.lines, 'SET_EQ') ieq_out = absmc.BranchLabel(stmt.lines, 'SET_EQ_OUT') absmc.BranchInstr('bz', ieq_set, reg) absmc.MoveInstr('move_immed_i', reg, 0, True) absmc.BranchInstr('jmp', ieq_out) ieq_set.add_to_code() absmc.MoveInstr('move_immed_i', reg, 1, True) ieq_out.add_to_code() if stmt.bop == 'and': and_skip = absmc.BranchLabel(stmt.lines, 'AND_SKIP') gen_code(stmt.arg1) reg = absmc.Register() absmc.MoveInstr('move', reg, stmt.arg1.end_reg) absmc.BranchInstr('bz', and_skip, stmt.arg1.end_reg) gen_code(stmt.arg2) absmc.MoveInstr('move', reg, stmt.arg2.end_reg) and_skip.add_to_code() if stmt.bop == 'or': or_skip = absmc.BranchLabel(stmt.lines, 'OR_SKIP') gen_code(stmt.arg1) reg = absmc.Register() absmc.MoveInstr('move', reg, stmt.arg1.end_reg) absmc.BranchInstr('bnz', or_skip, stmt.arg1.end_reg) gen_code(stmt.arg2) absmc.MoveInstr('move', reg, stmt.arg2.end_reg) or_skip.add_to_code() stmt.end_reg = reg elif isinstance(stmt, ast.ForStmt): # for-loop: # for (i = 0; i < 10; i++) { # body # } # set i's reg equal to 0 # create a label after this, as this is where we jump back to at end of loop # also create the 'out' label which is what we jump to when breaking out of loop # generate code for the 'cond' (test if i's reg is less than 10's reg) # test if the cond evaluated to false with 'bz', if so, break out of loop # else, fall through into the body of the for-loop # when body is over, generate code to update the var (i++) # jump unconditionally back to the cond_label, where we eval if i is still < 10 gen_code(stmt.init) cond_label = absmc.BranchLabel(stmt.lines, 'FOR_COND') current_enter_then_label = entry_label = absmc.BranchLabel(stmt.lines, 'FOR_ENTRY') current_loop_continue_label = continue_label = absmc.BranchLabel(stmt.lines, 'FOR_UPDATE') current_break_out_else_label = out_label = absmc.BranchLabel(stmt.lines, 'FOR_OUT') cond_label.add_to_code() gen_code(stmt.cond) absmc.BranchInstr('bz', out_label, stmt.cond.end_reg) entry_label.add_to_code() gen_code(stmt.body) continue_label.add_to_code() gen_code(stmt.update) absmc.BranchInstr('jmp', cond_label) out_label.add_to_code() elif isinstance(stmt, ast.AutoExpr): gen_code(stmt.arg) if stmt.when == 'post': tmp_reg = absmc.Register() absmc.MoveInstr('move', tmp_reg, stmt.arg.end_reg) one_reg = absmc.Register() # Load 1 into a register absmc.MoveInstr('move_immed_i', one_reg, 1, True) absmc.ArithInstr('add' if stmt.oper == 'inc' else 'sub', stmt.arg.end_reg, stmt.arg.end_reg, one_reg) if stmt.when == 'post': stmt.end_reg = tmp_reg else: stmt.end_reg = stmt.arg.end_reg elif isinstance(stmt, ast.SkipStmt): pass elif isinstance(stmt, ast.ReturnStmt): current_method.returned = True if stmt.expr is None: absmc.ProcedureInstr('ret') return gen_code(stmt.expr) # Load the result into a0 absmc.MoveInstr('move', absmc.Register('a', 0), stmt.expr.end_reg) # Return to caller absmc.ProcedureInstr('ret') elif isinstance(stmt, ast.WhileStmt): current_loop_continue_label = cond_label = absmc.BranchLabel(stmt.lines, 'WHILE_COND') current_enter_then_label = entry_label = absmc.BranchLabel(stmt.lines, 'WHILE_ENTRY') current_break_out_else_label = out_label = absmc.BranchLabel(stmt.lines, 'WHILE_OUT') cond_label.add_to_code() gen_code(stmt.cond) absmc.BranchInstr('bz', out_label, stmt.cond.end_reg) entry_label.add_to_code() gen_code(stmt.body) absmc.BranchInstr('jmp', cond_label) out_label.add_to_code() elif isinstance(stmt, ast.BreakStmt): absmc.BranchInstr('jmp', current_break_out_else_label) elif isinstance(stmt, ast.ContinueStmt): absmc.BranchInstr('jmp', current_loop_continue_label) elif isinstance(stmt, ast.IfStmt): # if (x == y) # ++x; # else # --x; # generate 2 labels, for the else part, and the out part # test if x == y # if not true, jump to the else part # if true, we're falling through to the then part, then must jump # out right before hitting the else part straight to the out part current_enter_then_label = then_part = absmc.BranchLabel(stmt.lines, 'THEN_PART') current_break_out_else_label = else_part = absmc.BranchLabel(stmt.lines, 'ELSE_PART') out_label = absmc.BranchLabel(stmt.lines, 'IF_STMT_OUT') gen_code(stmt.condition) absmc.BranchInstr('bz', else_part, stmt.condition.end_reg) then_part.add_to_code() gen_code(stmt.thenpart) absmc.BranchInstr('jmp', out_label) else_part.add_to_code() gen_code(stmt.elsepart) out_label.add_to_code() elif isinstance(stmt, ast.FieldAccessExpr): gen_code(stmt.base) cls = ast.lookup(ast.classtable, stmt.base.type.typename) field = ast.lookup(cls.fields, stmt.fname) offset_reg = absmc.Register() ret_reg = absmc.Register() absmc.MoveInstr('move_immed_i', offset_reg, field.offset, True) absmc.HeapInstr('hload', ret_reg, stmt.base.end_reg, offset_reg) stmt.offset_reg = offset_reg stmt.end_reg = ret_reg elif isinstance(stmt, ast.ClassReferenceExpr): stmt.end_reg = absmc.Register('sap') elif isinstance(stmt, ast.NewObjectExpr): recd_addr_reg = absmc.Register() size_reg = absmc.Register() absmc.MoveInstr('move_immed_i', size_reg, stmt.classref.size, True) absmc.HeapInstr('halloc', recd_addr_reg, size_reg) if stmt.constr_id is None: stmt.end_reg = recd_addr_reg return saved_regs = [] saved_regs.append(recd_addr_reg) # add a0 if the current method is not static if current_method.storage != 'static': saved_regs.append(absmc.Register('a', 0)) # for each var in each block of the current method, add to save list for block in range(0, len(current_method.vars.vars)): for var in current_method.vars.vars[block].values(): saved_regs.append(var.reg) # save each reg in the saved list for reg in saved_regs: absmc.ProcedureInstr('save', reg) absmc.MoveInstr('move', absmc.Register('a', 0), recd_addr_reg) arg_reg_index = 1 for arg in stmt.args: gen_code(arg) absmc.MoveInstr('move', absmc.Register('a', arg_reg_index), arg.end_reg) arg_reg_index += 1 absmc.ProcedureInstr('call', 'C_{}'.format(stmt.constr_id)) # restore regs from the now-reversed save list for reg in reversed(saved_regs): absmc.ProcedureInstr('restore', reg) stmt.end_reg = recd_addr_reg elif isinstance(stmt, ast.ThisExpr): stmt.end_reg = absmc.Register('a', 0) elif isinstance(stmt, ast.MethodInvocationExpr): gen_code(stmt.base) cls = ast.lookup(ast.classtable, stmt.base.type.typename) for method in cls.methods: if stmt.mname == method.name: break saved_regs = [] arg_reg_index = 0 # first arg goes into a1 if desired method is not static if method.storage != 'static': arg_reg_index += 1 # add a0 if the current method is not static if current_method.storage != 'static': saved_regs.append(absmc.Register('a', 0)) # for each var in each block of the current method, add to save list for block in range(0, len(current_method.vars.vars)): for var in current_method.vars.vars[block].values(): saved_regs.append(var.reg) # save each reg in the saved list for reg in saved_regs: absmc.ProcedureInstr('save', reg) if method.storage != 'static': absmc.MoveInstr('move', absmc.Register('a', 0), stmt.base.end_reg) for arg in stmt.args: gen_code(arg) absmc.MoveInstr('move', absmc.Register('a', arg_reg_index), arg.end_reg) arg_reg_index += 1 absmc.ProcedureInstr('call', 'M_{}_{}'.format(method.name, method.id)) # Store the result in a temporary register stmt.end_reg = absmc.Register() absmc.MoveInstr('move', stmt.end_reg, absmc.Register('a', 0)) # restore regs from the reversed save list for reg in reversed(saved_regs): absmc.ProcedureInstr('restore', reg) elif isinstance(stmt, ast.UnaryExpr): gen_code(stmt.arg) ret = absmc.Register() if stmt.uop == 'uminus': zero_reg = absmc.Register() if stmt.arg.type == ast.Type('float'): prefix = 'f' else: prefix = 'i' # if uminus, put 0 - <reg> into the return reg absmc.MoveInstr('move_immed_{}'.format(prefix), zero_reg, 0, True) absmc.ArithInstr('sub', ret, zero_reg, stmt.arg.end_reg, prefix) else: # if it's a 0, branch to set 1 # if it's a 1, we're falling through, setting to 0, and jumping out set_one_label = absmc.BranchLabel(stmt.lines, 'SET_ONE') out_label = absmc.BranchLabel(stmt.lines, 'UNARY_OUT') absmc.BranchInstr('bz', set_one_label, stmt.arg.end_reg) absmc.MoveInstr('move_immed_i', ret, 0, True) absmc.BranchInstr('jmp', out_label) set_one_label.add_to_code() absmc.MoveInstr('move_immed_i', ret, 1, True) out_label.add_to_code() stmt.end_reg = ret elif isinstance(stmt, ast.SuperExpr): stmt.end_reg = absmc.Register('a', 0) elif isinstance(stmt, ast.ArrayAccessExpr): # Create fake register. stmt.end_reg = absmc.Register('n', 0) print 'Found an array access. Arrays are not supported.' elif isinstance(stmt, ast.NewArrayExpr): # Create fake register. stmt.end_reg = absmc.Register('n', 0) print 'Found an array creation. Arrays are not supported.' else: print 'need instance ' + str(type(stmt)) pop_labels()
def lookup_field(self, fname): return ast.lookup(self.fields, fname)
def checkExpr(expr, Class): # check type errors fo constant expr if isinstance(expr, ConstantExpr): kind = expr.kind if kind == 'int': expr.type = 'int' elif kind == 'float': expr.type = 'float' elif kind == 'string': expr.type = 'string' elif kind == 'True' or kind == 'False': expr.type = 'boolean' elif kind == 'Null': expr.type = 'null' # check type errors for Var Expression elif isinstance(expr, VarExpr): var = expr.var expr.type = var.type # check type errors for Unary Expressions elif isinstance(expr, UnaryExpr): uop = expr.uop arg = expr.arg checkExpr(arg, Class) if str(arg.type) == 'int' and uop == 'uminus': expr.type = 'int' elif str(arg.type) == 'float' and uop == 'uminus': expr.type = 'float' elif str(arg.type) == 'boolean' and uop == 'neg': expr.type = 'boolean' else: expr.type = 'error' print "UnaryExpr Error at line %s: '%s' operation found invalid argument '%s'" % ( expr.lines, uop, arg.type) elif isinstance(expr, BinaryExpr): bop = expr.bop arg1 = expr.arg1 arg2 = expr.arg2 checkExpr(arg1, Class) checkExpr(arg2, Class) if bop == 'add' or bop == 'sub' or bop == 'mul' or bop == 'div': if (str(arg1.type) == 'int' and str(arg2.type) == 'int'): expr.type = 'int' elif (str(arg1.type) == 'int' or str(arg1.type) == 'float') and ( str(arg2.type) == 'int' or str(arg2.type) == 'float'): expr.type = 'float' else: expr.type = 'error' print "BinaryExpr Error at line %s: '%s' operation found invalid arguments '%s', '%s'" % ( expr.lines, bop, arg1.type, arg2.type) elif bop == 'and' or bop == 'or': if str(arg1.type) == 'boolean' and str(arg2.type) == 'boolean': expr.type = 'boolean' else: expr.type = 'error' print "BinaryExpr Error at line %s: '%s' operation found invalid arguments '%s', '%s'" % ( expr.lines, bop, arg1.type, arg2.type) elif bop == 'lt' or bop == 'leq' or bop == 'gt' or bop == 'geq': if (str(arg1.type) == 'int' or str(arg1.type) == 'float') and (str( arg2.type) == 'int' or str(arg2.type) == 'float'): expr.type = 'boolean' else: expr.type = 'error' print "BinaryExpr Error at line %s: '%s' operation found invalid arguments '%s', '%s'" % ( expr.lines, bop, arg1.type, arg2.type) elif bop == 'eq' or bop == 'neq': if isSubtype(arg1.type, arg2.type) or isSubtype( arg2.type, arg1.type): expr.type = 'boolean' else: expr.type = 'error' print "BinaryExpr Error at line %s: '%s' operation found invalid arguments '%s', '%s'" % ( expr.lines, bop, arg1.type, arg2.type) elif isinstance(expr, AssignExpr): lhs = expr.lhs rhs = expr.rhs checkExpr(lhs, Class) checkExpr(rhs, Class) if str(lhs.type) != 'error' and str(rhs.type) != 'error' and isSubtype( rhs.type, lhs.type): expr.type = rhs.type else: expr.type = 'error' if str(lhs.type) != 'error' and str(rhs.type) != 'error': print "AssignExpr Error at line %s: '%s' expression found when '%s' is expected" % ( expr.lines, rhs.type, lhs.type) elif isinstance(expr, AutoExpr): arg = expr.arg checkExpr(arg, Class) if str(arg.type) == 'int' or str(arg.type) == 'float': expr.type = arg.type else: expr.type = 'error' print "AutoExpr Error at line %s: '%s' found int or float expected" % ( expr.lines, arg.type) elif isinstance(expr, FieldAccessExpr): base = expr.base fname = expr.fname checkExpr(base, Class) if 'user' in str(base.type): A = str(base.type)[5:-1] storage = 'instance' elif 'class-literal' in str(base.type): A = str(base.type)[14:-1] storage = 'staic' classA = ast.lookup(ast.classtable, A) inSuperClass = False while (classA is not None): fields = classA.fields if fname in fields and fields[fname].storage is storage and ( not inSuperClass or fields[fname].visibility is 'public'): expr.resolvedID = fields[fname].id expr.type = fields[fname].type break else: classA = classA.superclass inSuperClass = True if classA is None: expr.type = 'error' expr.resolvedID = None print "FieldAccessExpr Error at line %s:, couldn't find field '%s'" % ( expr.lines, fname) elif isinstance(expr, MethodInvocationExpr): base = expr.base checkExpr(base, Class) mname = expr.mname args = expr.args if 'user' in str(base.type): A = str(base.type)[5:-1] storage = 'instance' elif 'class-literal' in str(base.type): A = str(base.type)[14:-1] storage = 'static' classA = ast.lookup(ast.classtable, A) inSuperClass = False argMatch = True methodFound = False isAllSubtype = True while (classA is not None): methods = classA.methods for m in methods: if m.name == mname and m.storage is storage and ( (not inSuperClass and str(classA.name) == str(Class.name)) or m.visibility is 'public'): formalPar = m.vars.vars[0] if len(args) == len(formalPar): argMatch = True for ID in range(0, len(formalPar)): for fp in formalPar: if formalPar[fp].id == ID + 1: checkExpr(args[ID], Class) if not isStrictSubtype( args[ID].type, formalPar[fp].type): ID = len(formalPar) argMatch = False break if argMatch and methodFound: expr.type = 'error' expr.resolvedID = None elif argMatch: methodFound = True expr.type = m.rtype expr.resolvedID = m.id if not methodFound: classA = classA.superclass inSuperClass = True else: break if not methodFound: classA = ast.lookup(ast.classtable, A) while (classA is not None): methods = classA.methods for m in methods: if m.name == mname and m.storage is storage and ( not inSuperClass or m.visibility is 'public'): formalPar = m.vars.vars[0] if len(args) == len(formalPar): argMatch = True isAllSubtype = True for ID in range(0, len(formalPar)): for fp in formalPar: if formalPar[fp].id == ID + 1: checkExpr(args[ID], Class) #if isStrictSubtype(args[ID].type, formalPar[fp].type): isAllSubtype = False if not isSubtype( args[ID].type, formalPar[fp].type): ID = len(formalPar) argMatch = False break if argMatch and methodFound and not isAllSubtype: expr.type = 'error' expr.resolvedID = None elif argMatch and not isAllSubtype: methodFound = True expr.type = m.rtype expr.resolvedID = m.id if not methodFound: classA = classA.superclass inSuperClass = True else: break if classA is None: expr.type = 'error' print "MethodInvocationExpr Error at line %s: couldn't find method with name '%s'" % ( expr.lines, mname) elif expr.type == 'error': print "MethodInvocationExpr Error at line %s: couldn't find valid method with name '%s'" % ( expr.lines, mname) elif isinstance(expr, NewObjectExpr): classref = expr.classref args = expr.args argMatch = True constructorFound = False inSuperClass = False constructors = classref.constructors for c in constructors: if c.visibility is 'public' or Class.name == classref.name: formalPar = c.vars.vars[0] if len(args) == len(formalPar): argMatch = True for ID in range(0, len(formalPar)): for fp in formalPar: if formalPar[fp].id == ID + 1: checkExpr(args[ID], Class) if not isStrictSubtype(args[ID].type, formalPar[fp].type): ID = len(formalPar) argMatch = False break if argMatch and constructorFound: expr.type = 'error' expr.resolvedID = None elif argMatch: constructorFound = True expr.resolvedID = c.id expr.type = 'user(' + classref.name + ')' if not constructorFound: for c in constructors: if c.visibility is 'public' or Class.name == classref.name: formalPar = c.vars.vars[0] if len(args) == len(formalPar): argMatch = True isAllSubtype = True for ID in range(0, len(formalPar)): for fp in formalPar: if formalPar[fp].id == ID + 1: checkExpr(args[ID], Class) # if isStrictSubtype(args[ID].type, formalPar[fp].type): isAllSubtype = False if not isSubtype(args[ID].type, formalPar[fp].type): ID = len(formalPar) argMatch = False break if argMatch and constructorFound and not isAllSubtype: expr.type = 'error' expr.resolvedID = None elif argMatch and not isAllSubtype: constructorFound = True expr.resolvedID = c.id expr.type = 'user(' + classref.name + ')' if not constructorFound: expr.type = 'error' print "NewObjectExpr Error at line %s: couldn't find valid class with name '%s'" % ( expr.lines, classref.name) elif isinstance(expr, ThisExpr): expr.type = 'user(' + Class.name + ')' elif isinstance(expr, SuperExpr): if Class.superclass.name is not None: expr.type = 'user(' + Class.superclass.name + ')' else: expr.type = 'error' print "ThisExpr Error at line %s" % (expr.lines) elif isinstance(expr, ClassReferenceExpr): classref = expr.classref expr.type = 'class-literal(' + classref.name + ')' elif isinstance(expr, ArrayAccessExpr): base = expr.base index = expr.index checkExpr(base, Class) checkExpr(index, Class) baseType = str(base.type) if 'array(' in baseType and index.type is 'int': expr.type = baseType[6:-1] else: expr.type = 'error' if index.type is not 'int': print "ArrayAccessExpr Error at line %s: 'int' index expected found %s" % ( expr.lines, index.type) else: print "ArrayAccessExpr Error at line %s: 'array(' base type expected found %s" % ( expr.lines, baseType) elif isinstance(expr, NewArrayExpr): baseType = expr.basetype args = expr.args checkExpr(baseType, Class) type = str(baseType) for arg in args: checkExpr(arg, Class) if str(arg.type) is 'int': type = 'array(' + type + ')' else: type = 'error' print "NewArrayExpr Error at line %s: 'int' argument expected found %s" % ( expr.lines, arg.type) break expr.type = type