1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/simplify/fixnames.py Sun May 27 18:25:25 2007 +0200
1.3 @@ -0,0 +1,489 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Fix name-related operations. The code in this module operates upon simplified
1.8 +program node trees.
1.9 +
1.10 +Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk>
1.11 +
1.12 +This software is free software; you can redistribute it and/or
1.13 +modify it under the terms of the GNU General Public License as
1.14 +published by the Free Software Foundation; either version 2 of
1.15 +the License, or (at your option) any later version.
1.16 +
1.17 +This software is distributed in the hope that it will be useful,
1.18 +but WITHOUT ANY WARRANTY; without even the implied warranty of
1.19 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.20 +GNU General Public License for more details.
1.21 +
1.22 +You should have received a copy of the GNU General Public
1.23 +License along with this library; see the file LICENCE.txt
1.24 +If not, write to the Free Software Foundation, Inc.,
1.25 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
1.26 +
1.27 +--------
1.28 +
1.29 +To use this module, the easiest approach is to use the fix function:
1.30 +
1.31 +fix(module)
1.32 +
1.33 +The more complicated approach involves instantiating a Fixer object:
1.34 +
1.35 +fixer = Fixer()
1.36 +
1.37 +Then, applying the fixer to an existing module:
1.38 +
1.39 +fixer.process(module)
1.40 +
1.41 +If a module containing built-in classes and functions exists, apply the fixer as
1.42 +follows:
1.43 +
1.44 +fixer.process(module, builtins)
1.45 +"""
1.46 +
1.47 +from simplify.simplified import *
1.48 +
1.49 +# Fixing of name-related operations.
1.50 +
1.51 +class Fixer(Visitor):
1.52 +
1.53 + """
1.54 + The name fixer which traverses the program nodes in a module, typically
1.55 + depth-first, and maintains a record of name usage in the different
1.56 + namespaces. As a consequence of various observations, some parts of the
1.57 + program node tree are modified with different operations employed to those
1.58 + originally defined.
1.59 +
1.60 + There are two kinds of subprograms in modules: functions/methods and
1.61 + internal subprograms which support things like loops. The latter kind of
1.62 + subprogram may acquire the locals from their callers and must therefore be
1.63 + traversed with information from such callers. Thus, we choose the top-level
1.64 + code and all functions/methods as roots for processing, following
1.65 + invocations of internal subprograms in order to reach all subprograms that
1.66 + are defined in each module.
1.67 +
1.68 + top-level
1.69 + ...
1.70 + invoke function
1.71 + ...
1.72 + invoke loop -> subprogram (internal)
1.73 + ...
1.74 +
1.75 + subprogram (function)
1.76 + ...
1.77 + invoke loop -> subprogram (internal)
1.78 + ...
1.79 +
1.80 + ...
1.81 +
1.82 + The above approach should guarantee that all subprograms are traversed and
1.83 + that all name lookups are correctly categorised.
1.84 + """
1.85 +
1.86 + def __init__(self):
1.87 +
1.88 + "Initialise the name fixer."
1.89 +
1.90 + Visitor.__init__(self)
1.91 +
1.92 + # Satisfy visitor issues.
1.93 +
1.94 + self.visitor = self
1.95 +
1.96 + def process(self, module, builtins=None):
1.97 +
1.98 + """
1.99 + Process the given 'module' optionally using some 'builtins' to reference
1.100 + built-in objects.
1.101 + """
1.102 +
1.103 + # The fixer maintains a list of transformed subprograms (added for each
1.104 + # of the processing "roots" and also for each invoked internal
1.105 + # subprogram), along with a list of current subprograms (used to avoid
1.106 + # recursion issues) and a list of current namespaces (used to recall
1.107 + # namespaces upon invoking internal subprograms).
1.108 +
1.109 + self.subprograms = []
1.110 + self.current_subprograms = []
1.111 + self.current_namespaces = []
1.112 +
1.113 + # First, process the top-level code, finding out which names are
1.114 + # defined at that level.
1.115 +
1.116 + self.global_namespace = None
1.117 + self.module = module
1.118 + self.builtins = builtins or module
1.119 +
1.120 + self.process_node(self.module)
1.121 +
1.122 + # Then, process all functions and methods, providing a global namespace.
1.123 + # By setting a global namespace, we influence the resolution of names:
1.124 + # those which are global to the top-level module (processed above) are
1.125 + # considered as built-in names, whereas those which are global to a
1.126 + # function or method are searched for in the global namespace.
1.127 +
1.128 + self.global_namespace = self.namespace
1.129 +
1.130 + for subprogram in self.module.simplifier.subprograms:
1.131 +
1.132 + # Internal subprograms are skipped here and processed specially via
1.133 + # Invoke nodes.
1.134 +
1.135 + if not getattr(subprogram, "internal", 0):
1.136 + self.subprograms.append(self.process_node(subprogram))
1.137 +
1.138 + # Ultimately, we redefine the list of subprograms on the visitor.
1.139 +
1.140 + self.module.simplifier.subprograms = self.subprograms
1.141 + return self.module
1.142 +
1.143 + def process_node(self, node, namespace=None):
1.144 +
1.145 + """
1.146 + Process a subprogram or module 'node', discovering from attributes on
1.147 + 'node' any initial locals. Return a modified subprogram or module.
1.148 + """
1.149 +
1.150 + # Do not process subprograms already being processed.
1.151 +
1.152 + if node in self.current_subprograms:
1.153 + return None
1.154 +
1.155 + # Obtain a namespace either based on locals or on a structure.
1.156 +
1.157 + structure = structure=getattr(node, "structure", None)
1.158 +
1.159 + # If passed some namespace, use that as the current namespace.
1.160 +
1.161 + if namespace is not None:
1.162 + self.namespace.merge_namespace(namespace)
1.163 + else:
1.164 + self.namespace = NameOrganiser(structure)
1.165 +
1.166 + # Record the current subprogram and namespace.
1.167 +
1.168 + self.current_subprograms.append(node)
1.169 + self.current_namespaces.append(self.namespace)
1.170 +
1.171 + # NOTE: Avoid PEP 227 (nested scopes) whilst permitting references to a
1.172 + # NOTE: subprogram within itself. Do not define the name of the function
1.173 + # NOTE: within a method definition.
1.174 +
1.175 + if isinstance(node, Subprogram) and getattr(node, "name", None) is not None and not getattr(node, "is_method", 0):
1.176 + self.namespace.store(node.name)
1.177 +
1.178 + # Register the names of parameters in the namespace.
1.179 +
1.180 + if hasattr(node, "params"):
1.181 + new_params = []
1.182 + for param, default in node.params:
1.183 + new_params.append((param, self.dispatch(default)))
1.184 + self.namespace.store(param)
1.185 + node.params = new_params
1.186 + if getattr(node, "star", None):
1.187 + param, default = node.star
1.188 + self.namespace.store(param)
1.189 + node.star = param, self.dispatch(default)
1.190 + if getattr(node, "dstar", None):
1.191 + param, default = node.dstar
1.192 + self.namespace.store(param)
1.193 + node.dstar = param, self.dispatch(default)
1.194 +
1.195 + # Add namespace details to any structure involved.
1.196 +
1.197 + if hasattr(node, "structure") and node.structure is not None:
1.198 +
1.199 + # Initialise bases where appropriate.
1.200 +
1.201 + if hasattr(node.structure, "bases"):
1.202 + bases = []
1.203 + for base in node.structure.bases:
1.204 + bases.append(self.dispatch(base))
1.205 + node.structure.bases = bases
1.206 +
1.207 + # Dispatch to the code itself.
1.208 +
1.209 + result = self.dispatch(node)
1.210 + result.organiser = self.namespace
1.211 +
1.212 + # Restore the previous subprogram and namespace.
1.213 +
1.214 + self.current_namespaces.pop()
1.215 + if self.current_namespaces:
1.216 + self.namespace = self.current_namespaces[-1]
1.217 + self.current_subprograms.pop()
1.218 +
1.219 + return result
1.220 +
1.221 + # Visitor methods.
1.222 +
1.223 + def default(self, node):
1.224 +
1.225 + """
1.226 + Process the given 'node', given that it does not have a specific
1.227 + handler.
1.228 + """
1.229 +
1.230 + for attr in ("pos_args",):
1.231 + value = getattr(node, attr, None)
1.232 + if value is not None:
1.233 + setattr(node, attr, self.dispatches(value))
1.234 + for attr in ("kw_args",):
1.235 + value = getattr(node, attr, None)
1.236 + if value is not None:
1.237 + setattr(node, attr, self.dispatch_dict(value))
1.238 + for attr in ("expr", "lvalue", "test", "star", "dstar"):
1.239 + value = getattr(node, attr, None)
1.240 + if value is not None:
1.241 + setattr(node, attr, self.dispatch(value))
1.242 + for attr in ("body", "else_", "handler", "finally_", "code", "choices", "nodes"):
1.243 + value = getattr(node, attr, None)
1.244 + if value is not None:
1.245 + setattr(node, attr, self.dispatches(value))
1.246 + return node
1.247 +
1.248 + def dispatch(self, node, *args):
1.249 + return Visitor.dispatch(self, node, *args)
1.250 +
1.251 + def visitGlobal(self, global_):
1.252 + for name in global_.names:
1.253 + self.namespace.make_global(name)
1.254 + return global_
1.255 +
1.256 + def visitLoadName(self, loadname):
1.257 +
1.258 + "Transform the 'loadname' node to a specific, scope-sensitive node."
1.259 +
1.260 + scope = self.namespace.find_for_load(loadname.name)
1.261 +
1.262 + # For structure namespaces, load an attribute.
1.263 +
1.264 + if scope == "structure":
1.265 + result = self.dispatch(
1.266 + LoadAttr(loadname.original, loadname.defining,
1.267 + expr=LoadRef(loadname.original,
1.268 + ref=self.namespace.structure),
1.269 + name=loadname.name,
1.270 + nstype="structure")
1.271 + )
1.272 +
1.273 + # For global accesses (ie. those outside the local namespace)...
1.274 +
1.275 + elif scope == "global":
1.276 +
1.277 + # Where a distinct global namespace exists, examine it.
1.278 +
1.279 + if self.global_namespace is not None:
1.280 + scope = self.global_namespace.find_for_load(loadname.name)
1.281 +
1.282 + # Where the name is outside the global namespace, it must be a
1.283 + # built-in.
1.284 +
1.285 + if scope == "global":
1.286 + result = self.dispatch(
1.287 + LoadAttr(loadname.original, loadname.defining,
1.288 + expr=LoadRef(loadname.original,
1.289 + ref=self.builtins),
1.290 + name=loadname.name,
1.291 + nstype="module")
1.292 + )
1.293 +
1.294 + # Otherwise, it is within the global namespace and must be a
1.295 + # global.
1.296 +
1.297 + else:
1.298 + result = self.dispatch(
1.299 + LoadAttr(loadname.original, loadname.defining,
1.300 + expr=LoadRef(loadname.original,
1.301 + ref=self.module),
1.302 + name=loadname.name,
1.303 + nstype="module")
1.304 + )
1.305 +
1.306 + # Where no global namespace exists, we are at the module level and
1.307 + # must be accessing a built-in.
1.308 +
1.309 + else:
1.310 + result = self.dispatch(
1.311 + LoadAttr(loadname.original, loadname.defining,
1.312 + expr=LoadRef(loadname.original,
1.313 + ref=self.builtins),
1.314 + name=loadname.name,
1.315 + nstype="module")
1.316 + )
1.317 +
1.318 + # For local accesses...
1.319 +
1.320 + else:
1.321 +
1.322 + # Where a distinct global namespace exists, it must be a local.
1.323 +
1.324 + if self.global_namespace is not None:
1.325 + result = loadname
1.326 +
1.327 + # Otherwise, we must be accessing a global (which is local at the
1.328 + # module level).
1.329 +
1.330 + else:
1.331 + result = self.dispatch(
1.332 + LoadAttr(loadname.original, loadname.defining,
1.333 + expr=LoadRef(loadname.original,
1.334 + ref=self.module),
1.335 + name=loadname.name,
1.336 + nstype="module")
1.337 + )
1.338 +
1.339 + return result
1.340 +
1.341 + def visitStoreName(self, storename):
1.342 +
1.343 + "Transform the 'storename' node to a specific, scope-sensitive node."
1.344 +
1.345 + scope = self.namespace.find_for_store(storename.name)
1.346 +
1.347 + # For structure namespaces, store an attribute.
1.348 +
1.349 + if scope == "structure":
1.350 + self.namespace.store(storename.name)
1.351 +
1.352 + return self.dispatch(
1.353 + StoreAttr(storename.original, storename.defining,
1.354 + lvalue=LoadRef(storename.original,
1.355 + ref=self.namespace.structure),
1.356 + name=storename.name,
1.357 + expr=storename.expr,
1.358 + nstype="structure")
1.359 + )
1.360 +
1.361 + # Where the name is outside the local namespace, disallow any built-in
1.362 + # assignment and store the name globally.
1.363 +
1.364 + elif scope == "global":
1.365 + return self.dispatch(
1.366 + StoreAttr(storename.original, storename.defining,
1.367 + lvalue=LoadRef(storename.original,
1.368 + ref=self.module),
1.369 + name=storename.name,
1.370 + expr=storename.expr,
1.371 + nstype="module")
1.372 + )
1.373 +
1.374 + # For local namespace accesses...
1.375 +
1.376 + else:
1.377 + self.namespace.store(storename.name)
1.378 +
1.379 + # If a distinct global namespace exists, it must be a local access.
1.380 +
1.381 + if self.global_namespace is not None:
1.382 + return storename
1.383 +
1.384 + # Otherwise, the name is being set at the module level and is
1.385 + # considered global.
1.386 +
1.387 + else:
1.388 + return self.dispatch(
1.389 + StoreAttr(storename.original, storename.defining,
1.390 + lvalue=LoadRef(storename.original,
1.391 + ref=self.module),
1.392 + name=storename.name,
1.393 + expr=storename.expr,
1.394 + nstype="module")
1.395 + )
1.396 +
1.397 + def visitInvokeFunction(self, invoke):
1.398 +
1.399 + "Transform the 'invoke' node, performing processing on subprograms."
1.400 +
1.401 + return self.default(invoke)
1.402 +
1.403 + def visitInvokeRef(self, invoke):
1.404 +
1.405 + "Transform the 'invoke' node, performing processing on subprograms."
1.406 +
1.407 + # The special case of internal subprogram invocation is addressed by
1.408 + # propagating namespace information to the subprogram and processing it.
1.409 +
1.410 + if invoke.share_locals:
1.411 + subprogram = self.process_node(invoke.ref, self.namespace)
1.412 + else:
1.413 + subprogram = self.process_node(invoke.ref)
1.414 +
1.415 + if subprogram is not None:
1.416 + self.subprograms.append(subprogram)
1.417 + return invoke
1.418 +
1.419 +class ScopeMismatch(Exception):
1.420 + pass
1.421 +
1.422 +class NameOrganiser:
1.423 +
1.424 + """
1.425 + A local namespace which may either relate to a genuine set of function
1.426 + locals or the initialisation of a structure.
1.427 + """
1.428 +
1.429 + def __init__(self, structure=None):
1.430 +
1.431 + "Initialise the namespace with an optional 'structure'."
1.432 +
1.433 + self.structure = structure
1.434 + if structure is not None:
1.435 + self.local = "structure"
1.436 + else:
1.437 + self.local = "local"
1.438 +
1.439 + # Names may be self.local or "global".
1.440 +
1.441 + self.names = {}
1.442 +
1.443 + def make_global(self, name):
1.444 + if not self.names.has_key(name):
1.445 + self.names[name] = "global"
1.446 + elif self.names[name] == self.local:
1.447 + raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.local)
1.448 +
1.449 + def find_for_load(self, name):
1.450 + return self.names.get(name, "global")
1.451 +
1.452 + def find_for_store(self, name):
1.453 + return self.names.get(name, self.local)
1.454 +
1.455 + def store(self, name):
1.456 + if self.names.get(name) != "global":
1.457 + self.names[name] = self.local
1.458 + else:
1.459 + raise ScopeMismatch, "Name '%s' already considered as global." % name
1.460 +
1.461 + def merge(self, name, scope):
1.462 + if self.names.get(name) in (None, scope):
1.463 + self.names[name] = scope
1.464 + else:
1.465 + raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.names[name])
1.466 +
1.467 + def merge_namespace(self, namespace):
1.468 + self.merge_items(namespace.names.items())
1.469 +
1.470 + def merge_items(self, items):
1.471 + for name, scope in items:
1.472 + self.merge(name, scope)
1.473 +
1.474 + def __repr__(self):
1.475 + return repr(self.names)
1.476 +
1.477 +# Convenience functions.
1.478 +
1.479 +def fix(module, builtins=None):
1.480 +
1.481 + """
1.482 + Fix the names in the given 'module', also employing the optional 'builtins'
1.483 + module, if specified.
1.484 + """
1.485 +
1.486 + fixer = Fixer()
1.487 + if builtins is not None:
1.488 + fixer.process(module, builtins)
1.489 + else:
1.490 + fixer.process(module)
1.491 +
1.492 +# vim: tabstop=4 expandtab shiftwidth=4