# HG changeset patch # User paulb@jeremy # Date 1155488263 -7200 # Node ID 222d7bf69b637f5c10a88e921a0c38039fcdd221 # Parent 7a364038f7828ed52a04fba85542e3c404279fe3 Fixed the NameOrganiser to correctly identify the scope of names. Added an nstype attribute to LoadAttr and StoreAttr nodes. diff -r 7a364038f782 -r 222d7bf69b63 fixnames.py --- a/fixnames.py Sat Aug 12 18:47:05 2006 +0200 +++ b/fixnames.py Sun Aug 13 18:57:43 2006 +0200 @@ -1,9 +1,8 @@ #!/usr/bin/env python """ -Fix name-related operations. The code in this module operates upon nodes which -are produced when simplifying AST node trees originating from the compiler -module. +Fix name-related operations. The code in this module operates upon simplified +program node trees. Copyright (C) 2006 Paul Boddie @@ -46,10 +45,36 @@ class Fixer(Visitor): """ - The name fixer which traverses the program nodes, typically depth-first, - and maintains a record of name usage in the different namespaces. As a - consequence of various observations, some parts of the program node tree are - modified with different operations employed to those originally defined. + The name fixer which traverses the program nodes in a module, typically + depth-first, and maintains a record of name usage in the different + namespaces. As a consequence of various observations, some parts of the + program node tree are modified with different operations employed to those + originally defined. + + There are two kinds of subprograms in modules: functions/methods and + internal subprograms which support things like loops. The latter kind of + subprogram may acquire the locals from their callers and must therefore be + traversed with information from such callers. Thus, we choose the top-level + code and all functions/methods as roots for processing, following + invocations of internal subprograms in order to reach all subprograms that + are defined in each module. + + top-level + ... + invoke function + ... + invoke loop -> subprogram (internal) + ... + + subprogram (function) + ... + invoke loop -> subprogram (internal) + ... + + ... + + The above approach should guarantee that all subprograms are traversed and + that all name lookups are correctly categorised. """ def __init__(self): @@ -69,6 +94,12 @@ 'builtins_visitor' to reference built-in objects. """ + # The fixer maintains a list of transformed subprograms (added for each + # of the processing "roots" and also for each invoked internal + # subprogram), along with a list of current subprograms (used to avoid + # recursion issues) and a list of current namespaces (used to recall + # namespaces upon invoking internal subprograms). + self.subprograms = [] self.current_subprograms = [] self.current_namespaces = [] @@ -78,13 +109,19 @@ self.global_namespace = None self.module = visitor.result + if builtins_visitor is not None: self.builtins_module = builtins_visitor.result else: self.builtins_module = None + self.process_node(visitor.result) # Then, process all functions and methods, providing a global namespace. + # By setting a global namespace, we influence the resolution of names: + # those which are global to the top-level module (processed above) are + # considered as built-in names, whereas those which are global to a + # function or method are searched for in the global namespace. self.global_namespace = self.namespace @@ -96,6 +133,8 @@ if not getattr(subprogram, "acquire_locals", 0): self.subprograms.append(self.process_node(subprogram)) + # Ultimately, we redefine the list of subprograms on the visitor. + visitor.subprograms = self.subprograms return visitor @@ -126,7 +165,7 @@ if namespace is not None: self.namespace.merge_namespace(namespace) - # NOTE: Check this. + # Register the names of parameters in the namespace. if hasattr(node, "params"): for param, default in node.params: @@ -153,6 +192,7 @@ # Dispatch to the code itself. result = self.dispatch(node) + result.organiser = self.namespace # Restore the previous subprogram and namespace. @@ -198,12 +238,12 @@ "Transform the 'loadname' node to a specific, scope-sensitive node." - scope = self.namespace.find(loadname.name) + 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)) + result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.namespace.structure), name=loadname.name, nstype="structure")) # For global accesses (ie. those outside the local namespace)... @@ -212,25 +252,25 @@ # Where a distinct global namespace exists, examine it. if self.global_namespace is not None: - scope = self.global_namespace.find(loadname.name) + 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(LoadAttr(expr=LoadRef(ref=self.builtins_module), name=loadname.name)) + result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.builtins_module), name=loadname.name, nstype="module")) # Otherwise, it is within the global namespace and must be a # global. else: - result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.module), name=loadname.name)) + result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.module), name=loadname.name, nstype="module")) # Where no global namespace exists, we are at the module level and # must be accessing a built-in. else: - result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.builtins_module), name=loadname.name)) + result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.builtins_module), name=loadname.name, nstype="module")) # For local accesses... @@ -245,7 +285,7 @@ # module level). else: - result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.module), name=loadname.name)) + result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.module), name=loadname.name, nstype="module")) return result @@ -253,18 +293,18 @@ "Transform the 'storename' node to a specific, scope-sensitive node." - scope = self.namespace.find(storename.name) + 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)) + return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.namespace.structure), name=storename.name, expr=storename.expr, nstype="structure")) # Where the name is outside the local namespace, disallow any built-in # assignment and store the name globally. elif scope == "global": - return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.module), name=storename.name, expr=storename.expr)) + return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.module), name=storename.name, expr=storename.expr, nstype="module")) # For local namespace accesses... @@ -280,7 +320,7 @@ # considered global. else: - return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.module), name=storename.name, expr=storename.expr)) + return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.module), name=storename.name, expr=storename.expr, nstype="module")) def visitInvoke(self, invoke): @@ -327,9 +367,12 @@ elif self.names[name] == self.local: raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.local) - def find(self, name): + def find_for_load(self, name): return self.names.get(name, "global") + def find_for_store(self, name): + return self.names.get(name, self.local) + def store(self, name): if self.names.get(name) != "global": self.names[name] = self.local diff -r 7a364038f782 -r 222d7bf69b63 simplified.py --- a/simplified.py Sat Aug 12 18:47:05 2006 +0200 +++ b/simplified.py Sun Aug 13 18:57:43 2006 +0200 @@ -56,6 +56,7 @@ index Any index involved (temporary variable name). value Any constant value. ref Any reference to (for example) subprograms. + nstype Any indication of the namespace type involved in a name access. Expression-related attributes: @@ -79,6 +80,12 @@ """ def __init__(self, original=None, **kw): + + """ + Initialise a program node with an optional link to an 'original' AST + node. + """ + self.original = original if self.original is not None: self.original._node = self @@ -106,7 +113,7 @@ def pprint(self, indent=0, continuation=None): self._pprint(indent, continuation, repr(self)) - # Show other details. + # Subprogram-related details. if hasattr(self, "params"): for name, default in self.params: @@ -121,6 +128,9 @@ self._pprint(indent + 2, "( ", "acquiring locals") if getattr(self, "structure", 0): self._pprint(indent + 2, "( ", "structure '%s'" % self.structure.name) + + # Statement-related details. + if hasattr(self, "test"): self.test.pprint(indent + 2, "? ") for attr in "code", "body", "else_", "handler", "finally_", "choices": @@ -129,6 +139,9 @@ for node in getattr(self, attr): node.pprint(indent + 2) self._pprint(indent, "", "}") + + # Expression-related details. + if hasattr(self, "expr"): self.expr.pprint(indent + 2, "- ") if hasattr(self, "nodes"): @@ -136,6 +149,8 @@ node.pprint(indent + 2, "- ") if hasattr(self, "lvalue"): self.lvalue.pprint(indent + 2, "= ") + if hasattr(self, "nstype"): + self._pprint(indent + 2, "", self.nstype) if hasattr(self, "args"): for arg in self.args: arg.pprint(indent + 2, "( ")