simplify

fixnames.py

184:7ea581069c2e
2007-01-25 paulb Fixed nested scopes workaround to only add function names to namespaces - not module names as well.
     1 #!/usr/bin/env python     2      3 """     4 Fix name-related operations. The code in this module operates upon simplified     5 program node trees.     6      7 Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk>     8      9 This software is free software; you can redistribute it and/or    10 modify it under the terms of the GNU General Public License as    11 published by the Free Software Foundation; either version 2 of    12 the License, or (at your option) any later version.    13     14 This software is distributed in the hope that it will be useful,    15 but WITHOUT ANY WARRANTY; without even the implied warranty of    16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    17 GNU General Public License for more details.    18     19 You should have received a copy of the GNU General Public    20 License along with this library; see the file LICENCE.txt    21 If not, write to the Free Software Foundation, Inc.,    22 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA    23     24 --------    25     26 To use this module, the easiest approach is to use the fix function:    27     28 fix(module)    29     30 The more complicated approach involves instantiating a Fixer object:    31     32 fixer = Fixer()    33     34 Then, applying the fixer to an existing module:    35     36 fixer.process(module)    37     38 If a module containing built-in classes and functions exists, apply the fixer as    39 follows:    40     41 fixer.process(module, builtins)    42 """    43     44 from simplified import *    45     46 # Fixing of name-related operations.    47     48 class Fixer(Visitor):    49     50     """    51     The name fixer which traverses the program nodes in a module, typically    52     depth-first, and maintains a record of name usage in the different    53     namespaces. As a consequence of various observations, some parts of the    54     program node tree are modified with different operations employed to those    55     originally defined.    56     57     There are two kinds of subprograms in modules: functions/methods and    58     internal subprograms which support things like loops. The latter kind of    59     subprogram may acquire the locals from their callers and must therefore be    60     traversed with information from such callers. Thus, we choose the top-level    61     code and all functions/methods as roots for processing, following    62     invocations of internal subprograms in order to reach all subprograms that    63     are defined in each module.    64     65     top-level    66         ...    67         invoke function    68         ...    69         invoke loop -> subprogram (internal)    70                            ...    71     72     subprogram (function)    73         ...    74         invoke loop -> subprogram (internal)    75                            ...    76     77     ...    78     79     The above approach should guarantee that all subprograms are traversed and    80     that all name lookups are correctly categorised.    81     """    82     83     def __init__(self):    84     85         "Initialise the name fixer."    86     87         Visitor.__init__(self)    88     89         # Satisfy visitor issues.    90     91         self.visitor = self    92     93     def process(self, module, builtins=None):    94     95         """    96         Process the given 'module' optionally using some 'builtins' to reference    97         built-in objects.    98         """    99    100         # The fixer maintains a list of transformed subprograms (added for each   101         # of the processing "roots" and also for each invoked internal   102         # subprogram), along with a list of current subprograms (used to avoid   103         # recursion issues) and a list of current namespaces (used to recall   104         # namespaces upon invoking internal subprograms).   105    106         self.subprograms = []   107         self.current_subprograms = []   108         self.current_namespaces = []   109    110         # First, process the top-level code, finding out which names are   111         # defined at that level.   112    113         self.global_namespace = None   114         self.module = module   115         self.builtins = builtins or module   116    117         self.process_node(self.module)   118    119         # Then, process all functions and methods, providing a global namespace.   120         # By setting a global namespace, we influence the resolution of names:   121         # those which are global to the top-level module (processed above) are   122         # considered as built-in names, whereas those which are global to a   123         # function or method are searched for in the global namespace.   124    125         self.global_namespace = self.namespace   126    127         for subprogram in self.module.simplifier.subprograms:   128    129             # Internal subprograms are skipped here and processed specially via   130             # Invoke nodes.   131    132             if not getattr(subprogram, "internal", 0):   133                 self.subprograms.append(self.process_node(subprogram))   134    135         # Ultimately, we redefine the list of subprograms on the visitor.   136    137         self.module.simplifier.subprograms = self.subprograms   138         return self.module   139    140     def process_node(self, node, namespace=None):   141    142         """   143         Process a subprogram or module 'node', discovering from attributes on   144         'node' any initial locals. Return a modified subprogram or module.   145         """   146    147         # Do not process subprograms already being processed.   148    149         if node in self.current_subprograms:   150             return None   151    152         # Obtain a namespace either based on locals or on a structure.   153    154         structure = structure=getattr(node, "structure", None)   155         self.namespace = NameOrganiser(structure)   156    157         # Record the current subprogram and namespace.   158    159         self.current_subprograms.append(node)   160         self.current_namespaces.append(self.namespace)   161    162         # If passed some namespace, merge its contents into this namespace.   163    164         if namespace is not None:   165             self.namespace.merge_namespace(namespace)   166    167         # NOTE: Avoid PEP 227 (nested scopes) whilst permitting references to a   168         # NOTE: subprogram within itself. Do not define the name of the function   169         # NOTE: within a method definition.   170    171         if isinstance(node, Subprogram) and getattr(node, "name", None) is not None and not getattr(node, "is_method", 0):   172             self.namespace.store(node.name)   173    174         # Register the names of parameters in the namespace.   175    176         if hasattr(node, "params"):   177             new_params = []   178             for param, default in node.params:   179                 new_params.append((param, self.dispatch(default)))   180                 self.namespace.store(param)   181             node.params = new_params   182         if getattr(node, "star", None):   183             param, default = node.star   184             self.namespace.store(param)   185             node.star = param, self.dispatch(default)   186         if getattr(node, "dstar", None):   187             param, default = node.dstar   188             self.namespace.store(param)   189             node.dstar = param, self.dispatch(default)   190    191         # Add namespace details to any structure involved.   192    193         if hasattr(node, "structure") and node.structure is not None:   194    195             # Initialise bases where appropriate.   196    197             if hasattr(node.structure, "bases"):   198                 bases = []   199                 for base in node.structure.bases:   200                     bases.append(self.dispatch(base))   201                 node.structure.bases = bases   202    203         # Dispatch to the code itself.   204    205         result = self.dispatch(node)   206         result.organiser = self.namespace   207    208         # Restore the previous subprogram and namespace.   209    210         self.current_namespaces.pop()   211         if self.current_namespaces:   212             self.namespace = self.current_namespaces[-1]   213         self.current_subprograms.pop()   214    215         return result   216    217     # Visitor methods.   218    219     def default(self, node):   220    221         """   222         Process the given 'node', given that it does not have a specific   223         handler.   224         """   225    226         for attr in ("pos_args",):   227             value = getattr(node, attr, None)   228             if value is not None:   229                 setattr(node, attr, self.dispatches(value))   230         for attr in ("kw_args",):   231             value = getattr(node, attr, None)   232             if value is not None:   233                 setattr(node, attr, self.dispatch_dict(value))   234         for attr in ("expr", "lvalue", "test", "star", "dstar"):   235             value = getattr(node, attr, None)   236             if value is not None:   237                 setattr(node, attr, self.dispatch(value))   238         for attr in ("body", "else_", "handler", "finally_", "code", "choices"):   239             value = getattr(node, attr, None)   240             if value is not None:   241                 setattr(node, attr, self.dispatches(value))   242         return node   243    244     def dispatch(self, node, *args):   245         return Visitor.dispatch(self, node, *args)   246    247     def visitGlobal(self, global_):   248         for name in global_.names:   249             self.namespace.make_global(name)   250         return global_   251    252     def visitLoadName(self, loadname):   253    254         "Transform the 'loadname' node to a specific, scope-sensitive node."   255    256         scope = self.namespace.find_for_load(loadname.name)   257    258         # For structure namespaces, load an attribute.   259    260         if scope == "structure":   261             result = self.dispatch(   262                 LoadAttr(loadname.original, loadname.defining,   263                     expr=LoadRef(loadname.original,   264                         ref=self.namespace.structure),   265                     name=loadname.name,   266                     nstype="structure")   267                 )   268    269         # For global accesses (ie. those outside the local namespace)...   270    271         elif scope == "global":   272    273             # Where a distinct global namespace exists, examine it.   274    275             if self.global_namespace is not None:   276                 scope = self.global_namespace.find_for_load(loadname.name)   277    278                 # Where the name is outside the global namespace, it must be a   279                 # built-in.   280    281                 if scope == "global":   282                     result = self.dispatch(   283                         LoadAttr(loadname.original, loadname.defining,   284                             expr=LoadRef(loadname.original,   285                                 ref=self.builtins),   286                             name=loadname.name,   287                             nstype="module")   288                         )   289    290                 # Otherwise, it is within the global namespace and must be a   291                 # global.   292    293                 else:   294                     result = self.dispatch(   295                         LoadAttr(loadname.original, loadname.defining,   296                             expr=LoadRef(loadname.original,   297                                 ref=self.module),   298                             name=loadname.name,   299                             nstype="module")   300                         )   301    302             # Where no global namespace exists, we are at the module level and   303             # must be accessing a built-in.   304    305             else:   306                 result = self.dispatch(   307                     LoadAttr(loadname.original, loadname.defining,   308                         expr=LoadRef(loadname.original,   309                             ref=self.builtins),   310                         name=loadname.name,   311                         nstype="module")   312                     )   313    314         # For local accesses...   315    316         else:   317    318             # Where a distinct global namespace exists, it must be a local.   319    320             if self.global_namespace is not None:   321                 result = loadname   322    323             # Otherwise, we must be accessing a global (which is local at the   324             # module level).   325    326             else:   327                 result = self.dispatch(   328                     LoadAttr(loadname.original, loadname.defining,   329                         expr=LoadRef(loadname.original,   330                             ref=self.module),   331                         name=loadname.name,   332                         nstype="module")   333                     )   334    335         return result   336    337     def visitStoreName(self, storename):   338    339         "Transform the 'storename' node to a specific, scope-sensitive node."   340    341         scope = self.namespace.find_for_store(storename.name)   342    343         # For structure namespaces, store an attribute.   344    345         if scope == "structure":   346             self.namespace.store(storename.name)   347    348             return self.dispatch(   349                 StoreAttr(storename.original, storename.defining,   350                     lvalue=LoadRef(storename.original,   351                         ref=self.namespace.structure),   352                     name=storename.name,   353                     expr=storename.expr,   354                     nstype="structure")   355                 )   356    357         # Where the name is outside the local namespace, disallow any built-in   358         # assignment and store the name globally.   359    360         elif scope == "global":   361             return self.dispatch(   362                 StoreAttr(storename.original, storename.defining,   363                     lvalue=LoadRef(storename.original,   364                         ref=self.module),   365                     name=storename.name,   366                     expr=storename.expr,   367                     nstype="module")   368                 )   369    370         # For local namespace accesses...   371    372         else:   373             self.namespace.store(storename.name)   374    375             # If a distinct global namespace exists, it must be a local access.   376    377             if self.global_namespace is not None:   378                 return storename   379    380             # Otherwise, the name is being set at the module level and is   381             # considered global.   382    383             else:   384                 return self.dispatch(   385                     StoreAttr(storename.original, storename.defining,   386                         lvalue=LoadRef(storename.original,   387                             ref=self.module),   388                         name=storename.name,   389                         expr=storename.expr,   390                         nstype="module")   391                     )   392    393     def visitInvokeFunction(self, invoke):   394    395         "Transform the 'invoke' node, performing processing on subprograms."   396    397         return self.default(invoke)   398    399     def visitInvokeBlock(self, invoke):   400    401         "Transform the 'invoke' node, performing processing on subprograms."   402    403         # The special case of internal subprogram invocation is addressed by   404         # propagating namespace information to the subprogram and processing it.   405    406         subprogram = self.process_node(invoke.expr.ref, self.namespace)   407         if subprogram is not None:   408             self.subprograms.append(subprogram)   409         return invoke   410    411 class ScopeMismatch(Exception):   412     pass   413    414 class NameOrganiser:   415    416     """   417     A local namespace which may either relate to a genuine set of function   418     locals or the initialisation of a structure.   419     """   420    421     def __init__(self, structure=None):   422    423         "Initialise the namespace with an optional 'structure'."   424    425         self.structure = structure   426         if structure is not None:   427             self.local = "structure"   428         else:   429             self.local = "local"   430    431         # Names may be self.local or "global".   432    433         self.names = {}   434    435     def make_global(self, name):   436         if not self.names.has_key(name):   437             self.names[name] = "global"   438         elif self.names[name] == self.local:   439             raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.local)   440    441     def find_for_load(self, name):   442         return self.names.get(name, "global")   443    444     def find_for_store(self, name):   445         return self.names.get(name, self.local)   446    447     def store(self, name):   448         if self.names.get(name) != "global":   449             self.names[name] = self.local   450         else:   451             raise ScopeMismatch, "Name '%s' already considered as global." % name   452    453     def merge(self, name, scope):   454         if self.names.get(name) in (None, scope):   455             self.names[name] = scope   456         else:   457             raise ScopeMismatch, "Name '%s' already considered as %s." % (name, self.names[name])   458    459     def merge_namespace(self, namespace):   460         self.merge_items(namespace.names.items())   461    462     def merge_items(self, items):   463         for name, scope in items:   464             self.merge(name, scope)   465    466     def __repr__(self):   467         return repr(self.names)   468    469 # Convenience functions.   470    471 def fix(module, builtins=None):   472    473     """   474     Fix the names in the given 'module', also employing the optional 'builtins'   475     module, if specified.   476     """   477    478     fixer = Fixer()   479     if builtins is not None:   480         fixer.process(module, builtins)   481     else:   482         fixer.process(module)   483    484 # vim: tabstop=4 expandtab shiftwidth=4