1.1 --- a/fixnames.py Sat Aug 12 18:47:05 2006 +0200
1.2 +++ b/fixnames.py Sun Aug 13 18:57:43 2006 +0200
1.3 @@ -1,9 +1,8 @@
1.4 #!/usr/bin/env python
1.5
1.6 """
1.7 -Fix name-related operations. The code in this module operates upon nodes which
1.8 -are produced when simplifying AST node trees originating from the compiler
1.9 -module.
1.10 +Fix name-related operations. The code in this module operates upon simplified
1.11 +program node trees.
1.12
1.13 Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk>
1.14
1.15 @@ -46,10 +45,36 @@
1.16 class Fixer(Visitor):
1.17
1.18 """
1.19 - The name fixer which traverses the program nodes, typically depth-first,
1.20 - and maintains a record of name usage in the different namespaces. As a
1.21 - consequence of various observations, some parts of the program node tree are
1.22 - modified with different operations employed to those originally defined.
1.23 + The name fixer which traverses the program nodes in a module, typically
1.24 + depth-first, and maintains a record of name usage in the different
1.25 + namespaces. As a consequence of various observations, some parts of the
1.26 + program node tree are modified with different operations employed to those
1.27 + originally defined.
1.28 +
1.29 + There are two kinds of subprograms in modules: functions/methods and
1.30 + internal subprograms which support things like loops. The latter kind of
1.31 + subprogram may acquire the locals from their callers and must therefore be
1.32 + traversed with information from such callers. Thus, we choose the top-level
1.33 + code and all functions/methods as roots for processing, following
1.34 + invocations of internal subprograms in order to reach all subprograms that
1.35 + are defined in each module.
1.36 +
1.37 + top-level
1.38 + ...
1.39 + invoke function
1.40 + ...
1.41 + invoke loop -> subprogram (internal)
1.42 + ...
1.43 +
1.44 + subprogram (function)
1.45 + ...
1.46 + invoke loop -> subprogram (internal)
1.47 + ...
1.48 +
1.49 + ...
1.50 +
1.51 + The above approach should guarantee that all subprograms are traversed and
1.52 + that all name lookups are correctly categorised.
1.53 """
1.54
1.55 def __init__(self):
1.56 @@ -69,6 +94,12 @@
1.57 'builtins_visitor' to reference built-in objects.
1.58 """
1.59
1.60 + # The fixer maintains a list of transformed subprograms (added for each
1.61 + # of the processing "roots" and also for each invoked internal
1.62 + # subprogram), along with a list of current subprograms (used to avoid
1.63 + # recursion issues) and a list of current namespaces (used to recall
1.64 + # namespaces upon invoking internal subprograms).
1.65 +
1.66 self.subprograms = []
1.67 self.current_subprograms = []
1.68 self.current_namespaces = []
1.69 @@ -78,13 +109,19 @@
1.70
1.71 self.global_namespace = None
1.72 self.module = visitor.result
1.73 +
1.74 if builtins_visitor is not None:
1.75 self.builtins_module = builtins_visitor.result
1.76 else:
1.77 self.builtins_module = None
1.78 +
1.79 self.process_node(visitor.result)
1.80
1.81 # Then, process all functions and methods, providing a global namespace.
1.82 + # By setting a global namespace, we influence the resolution of names:
1.83 + # those which are global to the top-level module (processed above) are
1.84 + # considered as built-in names, whereas those which are global to a
1.85 + # function or method are searched for in the global namespace.
1.86
1.87 self.global_namespace = self.namespace
1.88
1.89 @@ -96,6 +133,8 @@
1.90 if not getattr(subprogram, "acquire_locals", 0):
1.91 self.subprograms.append(self.process_node(subprogram))
1.92
1.93 + # Ultimately, we redefine the list of subprograms on the visitor.
1.94 +
1.95 visitor.subprograms = self.subprograms
1.96 return visitor
1.97
1.98 @@ -126,7 +165,7 @@
1.99 if namespace is not None:
1.100 self.namespace.merge_namespace(namespace)
1.101
1.102 - # NOTE: Check this.
1.103 + # Register the names of parameters in the namespace.
1.104
1.105 if hasattr(node, "params"):
1.106 for param, default in node.params:
1.107 @@ -153,6 +192,7 @@
1.108 # Dispatch to the code itself.
1.109
1.110 result = self.dispatch(node)
1.111 + result.organiser = self.namespace
1.112
1.113 # Restore the previous subprogram and namespace.
1.114
1.115 @@ -198,12 +238,12 @@
1.116
1.117 "Transform the 'loadname' node to a specific, scope-sensitive node."
1.118
1.119 - scope = self.namespace.find(loadname.name)
1.120 + scope = self.namespace.find_for_load(loadname.name)
1.121
1.122 # For structure namespaces, load an attribute.
1.123
1.124 if scope == "structure":
1.125 - result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.namespace.structure), name=loadname.name))
1.126 + result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.namespace.structure), name=loadname.name, nstype="structure"))
1.127
1.128 # For global accesses (ie. those outside the local namespace)...
1.129
1.130 @@ -212,25 +252,25 @@
1.131 # Where a distinct global namespace exists, examine it.
1.132
1.133 if self.global_namespace is not None:
1.134 - scope = self.global_namespace.find(loadname.name)
1.135 + scope = self.global_namespace.find_for_load(loadname.name)
1.136
1.137 # Where the name is outside the global namespace, it must be a
1.138 # built-in.
1.139
1.140 if scope == "global":
1.141 - result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.builtins_module), name=loadname.name))
1.142 + result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.builtins_module), name=loadname.name, nstype="module"))
1.143
1.144 # Otherwise, it is within the global namespace and must be a
1.145 # global.
1.146
1.147 else:
1.148 - result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.module), name=loadname.name))
1.149 + result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.module), name=loadname.name, nstype="module"))
1.150
1.151 # Where no global namespace exists, we are at the module level and
1.152 # must be accessing a built-in.
1.153
1.154 else:
1.155 - result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.builtins_module), name=loadname.name))
1.156 + result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.builtins_module), name=loadname.name, nstype="module"))
1.157
1.158 # For local accesses...
1.159
1.160 @@ -245,7 +285,7 @@
1.161 # module level).
1.162
1.163 else:
1.164 - result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.module), name=loadname.name))
1.165 + result = self.dispatch(LoadAttr(expr=LoadRef(ref=self.module), name=loadname.name, nstype="module"))
1.166
1.167 return result
1.168
1.169 @@ -253,18 +293,18 @@
1.170
1.171 "Transform the 'storename' node to a specific, scope-sensitive node."
1.172
1.173 - scope = self.namespace.find(storename.name)
1.174 + scope = self.namespace.find_for_store(storename.name)
1.175
1.176 # For structure namespaces, store an attribute.
1.177
1.178 if scope == "structure":
1.179 - return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.namespace.structure), name=storename.name, expr=storename.expr))
1.180 + return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.namespace.structure), name=storename.name, expr=storename.expr, nstype="structure"))
1.181
1.182 # Where the name is outside the local namespace, disallow any built-in
1.183 # assignment and store the name globally.
1.184
1.185 elif scope == "global":
1.186 - return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.module), name=storename.name, expr=storename.expr))
1.187 + return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.module), name=storename.name, expr=storename.expr, nstype="module"))
1.188
1.189 # For local namespace accesses...
1.190
1.191 @@ -280,7 +320,7 @@
1.192 # considered global.
1.193
1.194 else:
1.195 - return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.module), name=storename.name, expr=storename.expr))
1.196 + return self.dispatch(StoreAttr(lvalue=LoadRef(ref=self.module), name=storename.name, expr=storename.expr, nstype="module"))
1.197
1.198 def visitInvoke(self, invoke):
1.199
1.200 @@ -327,9 +367,12 @@
1.201 elif self.names[name] == self.local:
1.202 raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.local)
1.203
1.204 - def find(self, name):
1.205 + def find_for_load(self, name):
1.206 return self.names.get(name, "global")
1.207
1.208 + def find_for_store(self, name):
1.209 + return self.names.get(name, self.local)
1.210 +
1.211 def store(self, name):
1.212 if self.names.get(name) != "global":
1.213 self.names[name] = self.local