def add_var_to_env_class(self, var: SymbolNode, rtype: RType, base: Union[FuncInfo, ImplicitClass], reassign: bool = False) -> AssignmentTarget: # First, define the variable name as an attribute of the environment class, and then # construct a target for that attribute. self.fn_info.env_class.attributes[var.name] = rtype attr_target = AssignmentTargetAttr(base.curr_env_reg, var.name) if reassign: # Read the local definition of the variable, and set the corresponding attribute of # the environment class' variable to be that value. reg = self.read(self.environment.lookup(var), self.fn_info.fitem.line) self.add(SetAttr(base.curr_env_reg, var.name, reg, self.fn_info.fitem.line)) # Override the local definition of the variable to instead point at the variable in # the environment class. return self.environment.add_target(var, attr_target)
def load_outer_env(builder: IRBuilder, base: Value, outer_env: Environment) -> Value: """Loads the environment class for a given base into a register. Additionally, iterates through all of the SymbolNode and AssignmentTarget instances of the environment at the given index's symtable, and adds those instances to the environment of the current environment. This is done so that the current environment can access outer environment variables without having to reload all of the environment registers. Returns the register where the environment class was loaded. """ env = builder.add(GetAttr(base, ENV_ATTR_NAME, builder.fn_info.fitem.line)) assert isinstance(env.type, RInstance), '{} must be of type RInstance'.format(env) for symbol, target in outer_env.symtable.items(): env.type.class_ir.attributes[symbol.name] = target.type symbol_target = AssignmentTargetAttr(env, symbol.name) builder.environment.add_target(symbol, symbol_target) return env
def get_assignment_target(self, lvalue: Lvalue, line: int = -1) -> AssignmentTarget: if isinstance(lvalue, NameExpr): # If we are visiting a decorator, then the SymbolNode we really want to be looking at # is the function that is decorated, not the entire Decorator node itself. symbol = lvalue.node if isinstance(symbol, Decorator): symbol = symbol.func if symbol is None: # New semantic analyzer doesn't create ad-hoc Vars for special forms. assert lvalue.is_special_form symbol = Var(lvalue.name) if lvalue.kind == LDEF: if symbol not in self.environment.symtable: # If the function is a generator function, then first define a new variable # in the current function's environment class. Next, define a target that # refers to the newly defined variable in that environment class. Add the # target to the table containing class environment variables, as well as the # current environment. if self.fn_info.is_generator: return self.add_var_to_env_class( symbol, self.node_type(lvalue), self.fn_info.generator_class, reassign=False) # Otherwise define a new local variable. return self.environment.add_local_reg( symbol, self.node_type(lvalue)) else: # Assign to a previously defined variable. return self.environment.lookup(symbol) elif lvalue.kind == GDEF: globals_dict = self.load_globals_dict() name = self.load_static_unicode(lvalue.name) return AssignmentTargetIndex(globals_dict, name) else: assert False, lvalue.kind elif isinstance(lvalue, IndexExpr): # Indexed assignment x[y] = e base = self.accept(lvalue.base) index = self.accept(lvalue.index) return AssignmentTargetIndex(base, index) elif isinstance(lvalue, MemberExpr): # Attribute assignment x.y = e obj = self.accept(lvalue.expr) return AssignmentTargetAttr(obj, lvalue.name) elif isinstance(lvalue, TupleExpr): # Multiple assignment a, ..., b = e star_idx = None # type: Optional[int] lvalues = [] for idx, item in enumerate(lvalue.items): targ = self.get_assignment_target(item) lvalues.append(targ) if isinstance(item, StarExpr): if star_idx is not None: self.error("Two starred expressions in assignment", line) star_idx = idx return AssignmentTargetTuple(lvalues, star_idx) elif isinstance(lvalue, StarExpr): return self.get_assignment_target(lvalue.expr) assert False, 'Unsupported lvalue: %r' % lvalue