def substituted_expression(e, env): """ Return an expression with all freely occurring identifiers in E replaced by their corresponding value in ENV. """ from pltools import Environment subst = Environment(env) rewriter = VariableSubstitute(subst) return rewriter.rewrite(e)
def __init__(self, globals=None, tvsupply=None): # Record the global Python namespace (if any) in which code is # defined. This maps identifiers to values; by convention # Copperhead objects have a 'cu_type' slot that we will # use for typing information. # # This dictionary should never be modified. self.globals = globals or dict() # Supply of unique type variable names: #tv0, #tv1, #tv2, ... # They're known to be unique because they are illegal identifiers. self._tvsupply = tvsupply or name_supply(['#tv'], drop_zero=False) # The typings environment maps local identifiers to their # corresponding types. self.typings = Environment() # Type variables associated with formal parameters, both in # lambdas and procedures, are required to be monomorphic. This # set records all introduced type variables that are known to be # monomorphic. Since we make all internal variables unique, we # only need a single set for this, rather than a hierarchical # environment structure as with self.typings. self.monomorphs = set() # During inference, we accumulate a set of freely occurring # identifiers. This set contains AST nodes, rather than names. # Thus, multiple occurrences of a given name (e.g., 'x') may be # found in this set if they occurred at separate source # locations. For example, the expression 'x+x' will introduce # two free occurrences, not one. self.free_occurrences = set() # The inference system builds up a set of assumptions about the # types of identifiers occurring outside the current compilation # unit and which have no known 'cu_type' attribute. # This table provides a mapping from type variables created for # free occurrences to the AST node at which they occur. self.assumptions = dict()
class TypingContext: """ Encapsulate information passed around during type inference. Binding everything up in a structure like this makes it possible to add optional annotations without rewriting the parameter lists of all procedures involved in inference. """ def __init__(self, globals=None, tvsupply=None): # Record the global Python namespace (if any) in which code is # defined. This maps identifiers to values; by convention # Copperhead objects have a 'cu_type' slot that we will # use for typing information. # # This dictionary should never be modified. self.globals = globals or dict() # Supply of unique type variable names: #tv0, #tv1, #tv2, ... # They're known to be unique because they are illegal identifiers. self._tvsupply = tvsupply or name_supply(['#tv'], drop_zero=False) # The typings environment maps local identifiers to their # corresponding types. self.typings = Environment() # Type variables associated with formal parameters, both in # lambdas and procedures, are required to be monomorphic. This # set records all introduced type variables that are known to be # monomorphic. Since we make all internal variables unique, we # only need a single set for this, rather than a hierarchical # environment structure as with self.typings. self.monomorphs = set() # During inference, we accumulate a set of freely occurring # identifiers. This set contains AST nodes, rather than names. # Thus, multiple occurrences of a given name (e.g., 'x') may be # found in this set if they occurred at separate source # locations. For example, the expression 'x+x' will introduce # two free occurrences, not one. self.free_occurrences = set() # The inference system builds up a set of assumptions about the # types of identifiers occurring outside the current compilation # unit and which have no known 'cu_type' attribute. # This table provides a mapping from type variables created for # free occurrences to the AST node at which they occur. self.assumptions = dict() ###################################################################### # # The following methods provide convenient ways of working with the # state encapsulated in the TypingContext # def fresh_typevar(self): return T.Typevar(self._tvsupply.next()) def fresh_typevars(self, n): return [T.Typevar(n) for n in islice(self._tvsupply, n)] def fresh_typelist(self, keys, varmap=None): tvs = [self.fresh_typevar() for k in keys] if varmap: for k, v in zip(keys, tvs): varmap[k] = v return tvs def begin_scope(self): self.typings.begin_scope() def end_scope(self): self.typings.end_scope() def assuming(self, t, ast): assert t not in self.assumptions.keys() self.assumptions[t] = ast self.free_occurrences.add(ast) # XXX because monomorphs is scoped within functions, we treat # assumptions specially when generalizing bindings rather than # accumulating them here #self.monomorphs.add(t) ###################################################################### # # Following are the methods required by the unification interface. # def instantiate(tcon, t): 'Instantiate Polytypes as Monotypes with fresh type variables' if isinstance(t, T.Polytype): vars = tcon.fresh_typelist(t.variables) return T.substituted_type(t.monotype(), dict(zip(t.variables, vars))) else: return t def resolve_variable(tcon, t): 'Resolve current mapping (if any) of typevars' if isinstance(t, T.Typevar): return resolve(t, tcon.typings) else: return t def occurs_check(tcon, t1, t2): if T.occurs(t1, t2): raise InferenceError, "%s occurs in %s" % (t1, t2) def is_variable(tcon, t): return isinstance(t, T.Typevar) def error(tcon, msg): raise InferenceError, msg
class TypingContext: """ Encapsulate information passed around during type inference. Binding everything up in a structure like this makes it possible to add optional annotations without rewriting the parameter lists of all procedures involved in inference. """ def __init__(self, globals=None, tvsupply=None): # Record the global Python namespace (if any) in which code is # defined. This maps identifiers to values; by convention # Copperhead objects have a 'cu_type' slot that we will # use for typing information. # # This dictionary should never be modified. self.globals = globals or dict() # Supply of unique type variable names: #tv0, #tv1, #tv2, ... # They're known to be unique because they are illegal identifiers. self._tvsupply = tvsupply or name_supply(['#tv'], drop_zero=False) # The typings environment maps local identifiers to their # corresponding types. self.typings = Environment() # Type variables associated with formal parameters, both in # lambdas and procedures, are required to be monomorphic. This # set records all introduced type variables that are known to be # monomorphic. Since we make all internal variables unique, we # only need a single set for this, rather than a hierarchical # environment structure as with self.typings. self.monomorphs = set() # During inference, we accumulate a set of freely occurring # identifiers. This set contains AST nodes, rather than names. # Thus, multiple occurrences of a given name (e.g., 'x') may be # found in this set if they occurred at separate source # locations. For example, the expression 'x+x' will introduce # two free occurrences, not one. self.free_occurrences = set() # The inference system builds up a set of assumptions about the # types of identifiers occurring outside the current compilation # unit and which have no known 'cu_type' attribute. # This table provides a mapping from type variables created for # free occurrences to the AST node at which they occur. self.assumptions = dict() ###################################################################### # # The following methods provide convenient ways of working with the # state encapsulated in the TypingContext # def fresh_typevar(self): return T.Typevar(self._tvsupply.next()) def fresh_typevars(self, n): return [T.Typevar(n) for n in islice(self._tvsupply, n)] def fresh_typelist(self, keys, varmap=None): tvs = [self.fresh_typevar() for k in keys] if varmap: for k, v in zip(keys, tvs): varmap[k] = v return tvs def begin_scope(self): self.typings.begin_scope() def end_scope(self): self.typings.end_scope() def assuming(self, t, ast): assert t not in self.assumptions.keys() self.assumptions[t] = ast self.free_occurrences.add(ast) # XXX because monomorphs is scoped within functions, we treat # assumptions specially when generalizing bindings rather than # accumulating them here #self.monomorphs.add(t) ###################################################################### # # Following are the methods required by the unification interface. # def instantiate(tcon, t): 'Instantiate Polytypes as Monotypes with fresh type variables' if isinstance(t, T.Polytype): vars = tcon.fresh_typelist(t.variables) return T.substituted_type(t.monotype(), dict(zip(t.variables, vars))) else: return t def resolve_variable(tcon, t): 'Resolve current mapping (if any) of typevars' if isinstance(t, T.Typevar): return resolve(t, tcon.typings) else: return t def occurs_check(tcon, t1, t2): if T.occurs(t1, t2): raise InferenceError, "%s occurs in %s" % (t1,t2) def is_variable(tcon, t): return isinstance(t, T.Typevar) def error(tcon, msg): raise InferenceError, msg
def closure_conversion(ast, M): 'Perform closure conversion' env = Environment(M.globals, __builtin__.__dict__) return Front.closure_conversion(ast, env)
def free_variables(e, env={}): 'Generates all freely occurring identifiers in E not bound in ENV.' from pltools import Environment return FreeVariables(Environment(env)).visit(e)