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 _Lambda(self, e): fn = S.Name(self.names.next()) self.rewrite_children(e) body = S.Return(e.parameters[0]) self.proclist.append(S.Procedure(fn, e.variables, [body])) return fn
def _Apply(self, ast): fn_id = ast.function().id if fn_id in self.applies: args = ast.arguments() arity = len(args) return S.Apply(S.Name(fn_id + str(arity)), args) else: return ast
def make_name(arg): if not isinstance(arg, S.Tuple): return arg else: made_name = S.Name(names.next()) assembled = S.Bind(S.Tuple(*[make_name(x) for x in arg]), made_name) disassembly.insert(0, assembled) return made_name
def _Return(self, stmt): e = self.rewrite(stmt.value()) if isinstance(e, S.Name): stmt.parameters = [e] self.emit(stmt) return # If we're returning a tuple, we always copy the value into a return # variable. We may undo this later on, for entry-point procedures. ret = S.Name("result") self.emit(S.Bind(ret, e)) stmt.parameters = [ret] self.emit(stmt)
def _Bind(self, stmt): var = stmt.binder() varNames = [x.id for x in flatten(var)] operation = S.substituted_expression(stmt.value(), self.env) for name in varNames: if name not in self.exceptions: rename = '%s_%s' % (name, self.serial.next()) else: rename = name self.env[name] = S.Name(rename) result = S.Bind(S.substituted_expression(var, self.env), operation) return result
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 _Apply(self, ast): fn_id = ast.function().id arity = -1 if fn_id in self.applies: args = ast.arguments() arity = len(args) elif fn_id in self.binders: arity = self.arity assert (arity > 0) if arity > 0: return S.Apply(S.Name(fn_id + str(arity)), ast.arguments()) else: return ast
def _Expression(self, e): subexpressions = e.parameters e.parameters = [] for sub in subexpressions: sub = self.rewrite(sub) # XXX It doesn't seem right to include Closure on this list # of "atomic" values. But phase_assignment breaks if I # don't do this. if not isinstance(sub, (S.Name, S.Literal, S.Closure)): tn = S.Name(self.names.next()) self.emit(S.Bind(tn, sub)) else: tn = sub e.parameters.append(tn) return e
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] = bound 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 _Return(self, stmt): e = self.rewrite(stmt.value()) if isinstance(e, S.Name): # If we're returning one of the procedure formals unchanged, # we need to copy its value into a return variable. # Here is where we check: if e.id not in self.formals: #No need to copy value into a return variable stmt.parameters = [e] self.emit(stmt) return # If we're returning a tuple, we always copy the value into a return # variable. We may undo this later on, for entry-point procedures. ret = S.Name("result") self.emit(S.Bind(ret, e)) stmt.parameters = [ret] self.emit(stmt)
def build_cast(cast_name, args): "Helper function to build cast expressions" return S.Apply(S.Name(cast_name), args)
def _Map(self, ast): args = ast.parameters arity = len(args) - 1 assert (arity > 0) return S.Apply(S.Name('map' + str(arity)), args)
def mark_user(x): return S.mark_user(S.Name(x)).id