# HG changeset patch # User paulb@jeremy # Date 1155339995 -7200 # Node ID a7a001e803a3f744a06b08b21d8665f1955842e0 # Parent d129811690de73664ec692181d12a9014b8cd07b Changed the API of the Annotator and Fixer classes. Added LoadBuiltin. Changed the resolution of the scope for each name to consider built-in objects as being distinct from globals, and to consider module-level names to always be globals. Changed the name fixing activity to follow invocations in order to propagate locals to internal subprograms. Introduced more state-related attributes to the Annotator and Fixer in order to support re-entrancy. diff -r d129811690de -r a7a001e803a3 annotate.py --- a/annotate.py Sat Aug 12 01:42:41 2006 +0200 +++ b/annotate.py Sat Aug 12 01:46:35 2006 +0200 @@ -198,7 +198,20 @@ self.visitor = self - def process_all(self, visitor, builtins_visitor=None): + def process(self, visitor, builtins_visitor=None): + + """ + Process the resources of the given 'visitor', using the optional + 'builtins_visitor' to access built-in classes and functions. + """ + + self.subprograms = [] + self.current_subprograms = [] + self.current_namespaces = [] + self.current_returns = [] + self.current_return_locals = [] + self.current_temps = [] + self.current_types = [] # Give constants their own namespace. @@ -207,12 +220,16 @@ # Process the module, supplying builtins if possible. + self.global_namespace = Namespace() + if builtins_visitor is not None: - return self.process(visitor.result, builtins=builtins_visitor.result.namespace) + self.builtins_namespace = builtins_visitor.result.namespace else: - return self.process(visitor.result) + self.builtins_namespace = self.global_namespace - def process(self, node, locals=None, globals=None, builtins=None): + return self.process_node(visitor.result) + + def process_node(self, node, locals=None): """ Process a subprogram or module 'node', indicating any initial 'locals'. @@ -220,12 +237,19 @@ mutate nodes in the original program. """ - # Determine the global namespace. - # NOTE: Improve this. + if locals: + self.namespace = locals + else: + self.namespace = self.global_namespace + + # Record the current subprogram and namespace. - self.global_namespace = globals or Namespace() - self.builtins_namespace = builtins or self.global_namespace - self.namespace = locals or self.global_namespace + self.current_subprograms.append(node) + self.current_namespaces.append(self.namespace) + self.current_returns.append([]) + self.current_return_locals.append([]) + self.current_temps.append({}) + self.current_types.append([]) # Record the namespace on the node. # NOTE: This may eventually be a specialisation node. @@ -234,10 +258,10 @@ # Remember return values and locals snapshots. - self.returns = [] self.return_locals = [] - self.types = None - self.temp = {} + self.returns = self.current_returns[-1] + self.temp = self.current_temps[-1] + self.types = self.current_types[-1] # Add namespace details to any structure involved. @@ -256,6 +280,31 @@ # Dispatch to the code itself. result = self.dispatch(node) + + # Restore the previous subprogram and namespace. + + self.current_namespaces.pop() + if self.current_namespaces: + self.namespace = self.current_namespaces[-1] + + self.current_types.pop() + if self.current_types: + self.types = self.current_types[-1] + + self.current_temps.pop() + if self.current_temps: + self.temp = self.current_temps[-1] + + self.last_returns = self.current_returns.pop() + if self.current_returns: + self.returns = self.current_returns[-1] + + self.returned_locals = self.current_return_locals.pop() + if self.current_return_locals: + self.return_locals = self.current_return_locals[-1] + + self.current_subprograms.pop() + return result def annotate(self, node): @@ -315,13 +364,15 @@ return storename def visitLoadGlobal(self, loadglobal): - try: - self.types = self.global_namespace.load(loadglobal.name) - except KeyError: - self.types = self.builtins_namespace.load(loadglobal.name) + self.types = self.global_namespace.load(loadglobal.name) self.annotate(loadglobal) return loadglobal + def visitLoadBuiltin(self, loadbuiltin): + self.types = self.builtins_namespace.load(loadbuiltin.name) + self.annotate(loadbuiltin) + return loadbuiltin + def visitStoreGlobal(self, storeglobal): storeglobal.expr = self.dispatch(storeglobal.expr) self.global_namespace.merge(storeglobal.name, self.types) @@ -520,18 +571,16 @@ # Process the subprogram. - annotator = Annotator() - annotator.process(subprogram, namespace, self.global_namespace, self.builtins_namespace) + self.process_node(subprogram, namespace) - # NOTE: Annotate the node with invocation details. - # NOTE: This should really be as part of a table of alternatives. + # NOTE: Improve and verify this. if getattr(subprogram, "returns_value", 0): - self.types = annotator.returns + self.types = self.last_returns self.annotate(invoke) if getattr(invoke, "same_frame", 0): - for locals in annotator.return_locals: + for locals in self.returned_locals: self.namespace.merge_namespace(locals) # Remember the state of the system. diff -r d129811690de -r a7a001e803a3 fixnames.py --- a/fixnames.py Sat Aug 12 01:42:41 2006 +0200 +++ b/fixnames.py Sat Aug 12 01:46:35 2006 +0200 @@ -24,7 +24,6 @@ """ from simplified import * -import compiler # Fixing of name-related operations. @@ -38,41 +37,81 @@ """ def __init__(self): + + "Initialise the name fixer." + Visitor.__init__(self) # Satisfy visitor issues. self.visitor = self - def process_all(self, visitor): - subprograms = [] + def process(self, visitor): + + "Process the resources of the given 'visitor'." + + self.subprograms = [] + self.current_subprograms = [] + self.current_namespaces = [] + + # First, process the top-level code, finding out which names are + # defined at that level. + + self.global_namespace = None + visitor.result = self.process_node(visitor.result) + + # Then, process all functions and methods, providing a global namespace. + + self.global_namespace = self.namespace + for subprogram in visitor.subprograms: - subprograms.append(self.process(subprogram)) - visitor.subprograms = subprograms - visitor.result = self.process(visitor.result) + + # Internal subprograms are skipped here and processed specially via + # Invoke nodes. + + if not getattr(subprogram, "acquire_locals", 0): + self.subprograms.append(self.process_node(subprogram)) + + visitor.subprograms = self.subprograms return visitor - def process(self, node): + def process_node(self, node, namespace=None): """ Process a subprogram or module 'node', discovering from attributes on 'node' any initial locals. Return a modified subprogram or module. """ + # Do not process subprograms already being processed. + + if node in self.current_subprograms: + return None + # Obtain a namespace either based on locals or on a structure. - self.namespace = NameOrganiser(structure=getattr(node, "structure", None)) + structure = structure=getattr(node, "structure", None) + self.namespace = NameOrganiser(structure) + + # Record the current subprogram and namespace. + + self.current_subprograms.append(node) + self.current_namespaces.append(self.namespace) + + # If passed some namespace, merge its contents into this namespace. + + if namespace is not None: + self.namespace.merge_namespace(namespace) # NOTE: Check this. if hasattr(node, "params"): for param, default in node.params: self.namespace.store(param) - if hasattr(node, "star"): - param = node.star + if getattr(node, "star", None): + param, default = node.star self.namespace.store(param) - if hasattr(node, "dstar"): - param = node.dstar + if getattr(node, "dstar", None): + param, default = node.dstar self.namespace.store(param) # Add namespace details to any structure involved. @@ -90,6 +129,14 @@ # Dispatch to the code itself. result = self.dispatch(node) + + # Restore the previous subprogram and namespace. + + self.current_namespaces.pop() + if self.current_namespaces: + self.namespace = self.current_namespaces[-1] + self.current_subprograms.pop() + return result # Visitor methods. @@ -124,26 +171,107 @@ return global_ def visitLoadName(self, loadname): - print "Name", loadname.name, "in", self.namespace + + "Transform the 'loadname' node to a specific, scope-sensitive node." + scope = self.namespace.find_for_load(loadname.name) + + # For structure namespaces, load an attribute. + if scope == "structure": result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.namespace.structure), name=loadname.name)) + + # For global accesses (ie. those outside the local namespace)... + elif scope == "global": - result = self.dispatch(LoadGlobal(name=loadname.name)) + + # Where a distinct global namespace exists, examine it. + + if self.global_namespace is not None: + scope = self.global_namespace.find_for_load(loadname.name) + + # Where the name is outside the global namespace, it must be a + # built-in. + + if scope == "global": + result = self.dispatch(LoadBuiltin(name=loadname.name)) + + # Otherwise, it is within the global namespace and must be a + # global. + + else: + result = self.dispatch(LoadGlobal(name=loadname.name)) + + # Where no global namespace exists, we are at the module level and + # must be accessing a built-in. + + else: + result = self.dispatch(LoadBuiltin(name=loadname.name)) + + # For local accesses... + else: - result = loadname + + # Where a distinct global namespace exists, it must be a local. + + if self.global_namespace is not None: + result = loadname + + # Otherwise, we must be accessing a global (which is local at the + # module level). + + else: + result = self.dispatch(LoadGlobal(name=loadname.name)) + return result def visitStoreName(self, storename): + + "Transform the 'storename' node to a specific, scope-sensitive node." + scope = self.namespace.find_for_store(storename.name) + + # For structure namespaces, store an attribute. + if scope == "structure": return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.namespace.structure), name=storename.name, expr=storename.expr)) + + # Where the name is outside the local namespace, disallow any built-in + # assignment and store the name globally. + elif scope == "global": return self.dispatch(StoreGlobal(name=storename.name, expr=storename.expr)) + + # For local namespace accesses... + else: - storename.expr = self.dispatch(storename.expr) self.namespace.store(storename.name) - return storename + + # If a distinct global namespace exists, it must be a local access. + + if self.global_namespace is not None: + return storename + + # Otherwise, the name is being set at the module level and is + # considered global. + + else: + return self.dispatch(StoreGlobal(name=storename.name, expr=storename.expr)) + + def visitInvoke(self, invoke): + + "Transform the 'invoke' node, performing processing on subprograms." + + # The special case of internal subprogram invocation is addressed by + # propagating namespace information to the subprogram and processing it. + + if getattr(invoke, "same_frame", 0): + subprogram = self.process_node(invoke.expr.ref, self.namespace) + if subprogram is not None: + self.subprograms.append(subprogram) + return invoke + else: + return self.default(invoke) class NameOrganiser: @@ -177,9 +305,9 @@ else: return "global" - def store(self, name, types=None): + def store(self, name): if name not in self.not_local: - self.names[name] = types + self.names[name] = None else: raise KeyError, name @@ -189,6 +317,17 @@ else: return self.names[name] + def merge(self, name): + if not self.names.has_key(name): + self.names[name] = None + + def merge_namespace(self, namespace): + self.merge_names(namespace.names.keys()) + + def merge_names(self, names): + for name in names: + self.merge(name) + def __repr__(self): return repr(self.names) diff -r d129811690de -r a7a001e803a3 simplified.py --- a/simplified.py Sat Aug 12 01:42:41 2006 +0200 +++ b/simplified.py Sat Aug 12 01:46:35 2006 +0200 @@ -164,6 +164,7 @@ class LoadTemp(Node): "Load a previously-stored temporary value." class LoadName(Node): "Load a named object." class LoadGlobal(Node): "Load a named global object." +class LoadBuiltin(Node): "Load a named built-in object." class LoadAttr(Node): "Load an object attribute." class LoadRef(Node): "Load a reference, typically a subprogram or a constant." class LoadExc(Node): "Load a handled exception." diff -r d129811690de -r a7a001e803a3 test.py --- a/test.py Sat Aug 12 01:42:41 2006 +0200 +++ b/test.py Sat Aug 12 01:46:35 2006 +0200 @@ -10,8 +10,8 @@ v = module_simplifier = simplify.Simplifier() module_simplifier.process(m) builtins_fixer = fixnames.Fixer() -builtins_fixer.process_all(builtins_simplifier) +builtins_fixer.process(builtins_simplifier) module_fixer = fixnames.Fixer() -module_fixer.process_all(module_simplifier) +module_fixer.process(module_simplifier) rb = builtins_simplifier.result r = module_simplifier.result