def _Closure(self, c): closed_over_literal = any( map(lambda x: not isinstance(x, S.Name), c.closed_over())) if not closed_over_literal: return c #Find procedure being closed over proc_name = c.body().id proc = self.procedures[proc_name] proc_args = proc.formals() closed_args = c.closed_over() #Construct new set of arguments, with literals closed over removed replaced_args = proc_args[:-len(closed_args)] replaced_closed_over = [] #Also record what replacements to make replacement = {} for orig_arg, closed_arg in zip(proc_args[-len(closed_args):], closed_args): if isinstance(closed_arg, S.Name): replaced_args.append(orig_arg) replaced_closed_over.append(closed_arg) else: replacement[orig_arg.id] = closed_arg #If we are only closing over literals, we will return a name #rather than a reduced closure. Check. fully_opened = len(replacement) == len(closed_args) replaced_stmts = [ S.substituted_expression(si, replacement) \ for si in proc.body()] replaced_name = S.Name(proc_name + self.name_supply.next()) self.propagated.append( S.Procedure(replaced_name, replaced_args, replaced_stmts)) if fully_opened: return replaced_name else: return S.Closure(replaced_closed_over, replaced_name)
def _Procedure(self, ast): binders = [v.id for v in flatten(ast.variables)] # NOTE: this includes name _ClosureRecursion._Procedure(self, ast) # Take the free variable list, stick it in a set to make sure we don't # duplicate a variable, and then put it back in a list to make sure # it's got a defined ordering, which sets don't have free = list( set([ v for v in S.free_variables(ast.body(), binders) if v in self.env ])) if free: bound = [S.Name("_K%d" % i) for i in range(len(free))] ast.variables = ast.variables + bound ast.parameters = S.substituted_expression(ast.parameters, dict(zip(free, bound))) # Transform recursive calls of this procedure within its own body. recursive = _ClosureRecursion(self.env) self.env[ast.name().id] = S.Closure(bound, ast.name()) ast.parameters = recursive.rewrite(ast.parameters) # Register rewrite for calls to this procedure in later # parts of the defining scope self.env[ast.name().id] = S.Closure([S.Name(x) for x in free], ast.name()) # else: # self.locally_bound([ast.name()]) return ast
def _Lambda(self, e): _ClosureRecursion._Lambda(self, e) formals = [v.id for v in flatten(e.formals())] # Take the free variable list, stick it in a set to make sure we don't # duplicate a variable, and then put it back in a list to make sure # it's got a defined ordering, which sets don't have free = list( set([ v for v in S.free_variables(e.body(), formals) if v in self.env ])) if free: bound = [S.Name("_K%d" % i) for i in range(len(free))] body = S.substituted_expression(e.body(), dict(zip(free, bound))) e.parameters = [body] e.variables = e.variables + bound return S.Closure([S.Name(x) for x in free], e) else: return e
def _Name(self, ast): x = getattr(self.env, ast.id, None) if ast.id in self.env and isinstance(self.env[ast.id], S.Closure): return S.Closure(self.env[ast.id].variables, ast) else: return ast