simplify

simplify.py

185:bcff64bc6ffa
2007-01-26 paulb Consolidated original node information in the original attribute, adding it for all invocations so that instance lookup may still work. Renamed _new_instance to new_instance, removing the old new_instance method. Increased the recursion limit.
     1 #!/usr/bin/env python     2      3 """     4 Simplify AST structures for easier type propagation and analysis. The code in     5 this module processes AST trees originating from the compiler module and     6 produces a result tree consisting of instruction-oriented program nodes.     7      8 Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>     9     10 This software is free software; you can redistribute it and/or    11 modify it under the terms of the GNU General Public License as    12 published by the Free Software Foundation; either version 2 of    13 the License, or (at your option) any later version.    14     15 This software is distributed in the hope that it will be useful,    16 but WITHOUT ANY WARRANTY; without even the implied warranty of    17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    18 GNU General Public License for more details.    19     20 You should have received a copy of the GNU General Public    21 License along with this library; see the file LICENCE.txt    22 If not, write to the Free Software Foundation, Inc.,    23 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA    24     25 --------    26     27 To use this module, the easiest approach is to use the simplify function:    28     29 simplify(filename)    30     31 The more complicated approach involves first instantiating a Simplifier object:    32     33 simplifier = Simplifier()    34     35 Then, applying the simplifier to an AST tree:    36     37 module = compiler.parseFile(filename)    38 simplifier.process(module)    39 """    40     41 from simplified import *    42 import compiler.ast    43 import os    44     45 class Simplifier(Visitor):    46     47     """    48     A simplifying visitor for AST nodes.    49     50     Covered: Add, And, AssAttr, AssList, AssName, AssTuple, Assign, AugAssign,    51              Break, CallFunc, Class, Compare, Const, Continue, Dict, Discard,    52              Div, FloorDiv, For, From, Function, Getattr, Global, If, Import,    53              Invert, Keyword, Lambda, List, Mod, Module, Mul, Name, Not, Or,    54              Pass, Power, Print, Printnl, Raise, Return, Slice, Stmt, Sub,    55              Subscript, TryExcept, TryFinally, Tuple, While, UnaryAdd, UnarySub.    56     57     Missing: Assert, Backquote, Bitand, Bitor, Bitxor, Decorators, Ellipsis,    58              Exec, LeftShift, ListComp, ListCompFor, ListCompIf, RightShift,    59              Sliceobj, Yield.    60     """    61     62     def __init__(self, builtins=0):    63     64         """    65         Initialise the simplifier with the optional 'builtins' parameter    66         indicating whether the module contains the built-in classes and    67         functions.    68         """    69     70         Visitor.__init__(self)    71         self.subprograms = []           # Subprograms outside the tree.    72         self.structures = []            # Structures/classes.    73         self.constants = {}             # Constants.    74         self.current_subprograms = []   # Current subprograms being processed.    75         self.current_structures = []    # Current structures being processed.    76         self.within_class = 0           # Whether a class is being defined.    77         self.builtins = builtins        # Whether the builtins are being processed.    78     79         # Convenience attributes.    80     81         self.subnames = {}    82     83         # For compiler package mechanisms.    84     85         self.visitor = self    86     87     def process(self, node, name):    88         result = self.dispatch(node, name)    89         result.simplifier = self    90         return result    91     92     def dispatch_or_none(self, node, *args):    93         if node is not None:    94             return self.dispatch(node, *args)    95         else:    96             return LoadName(node, name="None")    97     98     # Top-level transformation.    99    100     def visitModule(self, module, name=None):   101    102         """   103         Process the given 'module', producing a Module object which contains the   104         resulting program nodes. If the optional 'name' is provided, the 'name'   105         attribute is set on the Module object using a value other than None.   106         """   107    108         result = self.module = Module(module, 1, name=name)   109         module_code = self.dispatch(module.node)   110    111         # NOTE: Constant initialisation necessary for annotation but perhaps   112         # NOTE: redundant in the program.   113    114         init_code = []   115         for value, constant in self.constants.items():   116             init_code.append(   117                 StoreAttr(   118                     lvalue=LoadRef(ref=constant),   119                     name="__class__",   120                     expr=LoadName(name=constant.typename)   121                     )   122                 )   123    124         # NOTE: Hack to ensure correct initialisation of constants.   125    126         if self.builtins:   127             result.code = module_code + init_code   128         else:   129             result.code = init_code + module_code    130         return result   131    132     # Node transformations.   133    134     def visitAdd(self, add):   135         return self._visitBinary(add, "__add__", "__radd__")   136    137     def visitAnd(self, and_):   138    139         """   140         Make a subprogram for the 'and_' node and record its contents inside the   141         subprogram. Convert...   142    143         And (test)   144             (test)   145             ...   146    147         ...to:   148    149         Subprogram -> Conditional (test) -> ReturnFromBlock ...   150                                   (else) -> Conditional (test) -> ReturnFromBlock ...   151                                                         (else) -> ...   152         """   153    154         subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)   155         self.current_subprograms.append(subprogram)   156    157         # In the subprogram, make instructions which store each operand, test   158         # for each operand's truth status, and if appropriate return from the   159         # subprogram with the value of the operand.   160    161         last = and_.nodes[-1]   162         results = nodes = []   163    164         for node in and_.nodes:   165             expr = self.dispatch(node)   166    167             # Return from the subprogram where the test is not satisfied.   168    169             if node is not last:   170                 nodes += [   171                     StoreTemp(expr=expr),   172                     Conditional(   173                         test=self._visitNot(LoadTemp()),   174                         body=[   175                             ReturnFromBlock(   176                                 expr=LoadTemp()   177                                 )   178                             ],   179                         else_=[   180                             ReleaseTemp()   181                             # Subsequent operations go here!   182                             ]   183                         )   184                     ]   185    186                 # Put subsequent operations in the else section of this conditional.   187    188                 nodes = nodes[-1].else_   189    190             # For the last operation, return the result.   191    192             else:   193                 nodes.append(ReturnFromBlock(expr=expr))   194    195         # Finish the subprogram definition.   196    197         subprogram.code = results   198    199         self.current_subprograms.pop()   200         self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram   201    202         # Make an invocation of the subprogram.   203    204         result = InvokeBlock(and_, 1, produces_result=1)   205         result.expr = LoadRef(ref=subprogram)   206         return result   207    208     # Assignments.   209    210     def visitAssAttr(self, assattr, in_sequence=0):   211         expr = self._visitAssNameOrAttr(assattr, in_sequence)   212         lvalue = self.dispatch(assattr.expr)   213         result = StoreAttr(assattr, 1, name=assattr.attrname, lvalue=lvalue, expr=expr)   214         return result   215    216     def visitAssign(self, assign):   217         result = Assign(assign, 1)   218         store = StoreTemp(expr=self.dispatch(assign.expr))   219         release = ReleaseTemp()   220         result.code = [store] + self.dispatches(assign.nodes, 0) + [release]   221         return result   222    223     def visitAssList(self, asslist, in_sequence=0):   224         if not in_sequence:   225             expr = LoadTemp()   226         else:   227             expr = InvokeFunction(asslist, expr=LoadAttr(expr=LoadTemp(), name="next"))   228         result = Assign(asslist, 1)   229         store = StoreTemp(expr=InvokeFunction(asslist, expr=LoadAttr(name="__iter__", expr=expr)))   230         release = ReleaseTemp()   231         result.code = [store] + self.dispatches(asslist.nodes, 1) + [release]   232         return result   233    234     visitAssTuple = visitAssList   235    236     def _visitAssNameOrAttr(self, node, in_sequence):   237         if not in_sequence:   238             return LoadTemp()   239         else:   240             return InvokeFunction(node, expr=LoadAttr(expr=LoadTemp(), name="next"))   241    242     def visitAssName(self, assname, in_sequence=0):   243         expr = self._visitAssNameOrAttr(assname, in_sequence)   244         result = StoreName(assname, 1, name=assname.name, expr=expr)   245         return result   246    247     augassign_methods = {   248         "+=" : "__iadd__", "-=" : "__isub__", "*=" : "__imul__", "/=" : "__idiv__",   249         "%=" : "__imod__", "**=" : "__ipow__", "<<=" : "__ilshift__", ">>=" : "__irshift__",   250         "&=" : "__iand__", "^=" : "__ixor__", "|=" : "__ior__"   251         }   252    253     def visitAugAssign(self, augassign):   254    255         """   256         Convert the augmented assignment...   257    258         AugAssign (node) -> Name | Getattr | Slice | Subscript   259                   (op)   260                   (expr)   261    262         ...to:   263    264         Assign (code) -> StoreTemp (expr) -> InvokeFunction (expr) -> LoadAttr (expr) -> <name>   265                                                                                (name) -> <op>   266                          StoreName (name) -> <name>   267                                    (expr) -> LoadTemp   268                          ReleaseTemp   269         """   270    271         result = Assign(augassign, 1)   272         expr = self.dispatch(augassign.expr)   273    274         # Simple augmented assignment: name += expr   275    276         if isinstance(augassign.node, compiler.ast.Name):   277             result.code = [   278                 StoreTemp(   279                     expr=InvokeFunction( # referenced below   280                         augassign,   281                         args=[expr],   282                         star=None,   283                         dstar=None,   284                         expr=LoadAttr(   285                             expr=self.dispatch(augassign.node),   286                             name=self.augassign_methods[augassign.op]   287                             )   288                         )   289                     ),   290                 StoreName(   291                     expr=LoadTemp(),   292                     name=augassign.node.name),   293                 ReleaseTemp()   294                 ]   295    296             # Make nice annotations for the viewer.   297    298             augassign._op_call = result.code[0].expr   299    300         # Complicated augmented assignment: lvalue.attr += expr   301    302         elif isinstance(augassign.node, compiler.ast.Getattr):   303    304             # <lvalue> -> setattr(<lvalue>, getattr(<lvalue>, "attr").__xxx__(expr))   305    306             result.code = [   307                 StoreTemp(   308                     index="expr",   309                     expr=self.dispatch(augassign.node.expr)   310                     ),   311                 StoreTemp(   312                     expr=InvokeFunction( # referenced below   313                         augassign,   314                         args=[expr], star=None, dstar=None,   315                         expr=LoadAttr(   316                             expr=LoadAttr(augassign.node, 1,   317                                 expr=LoadTemp(index="expr"),   318                                 name=augassign.node.attrname   319                                 ),   320                             name=self.augassign_methods[augassign.op]   321                             )   322                         )   323                     ),   324                 StoreAttr(   325                     expr=LoadTemp(),   326                     lvalue=LoadTemp(index="expr"),   327                     name=augassign.node.attrname   328                     ),   329                 ReleaseTemp(index="expr"),   330                 ReleaseTemp()   331                 ]   332    333             # Make nice annotations for the viewer.   334    335             augassign._op_call = result.code[1].expr   336    337         # Complicated augassign using slices: lvalue[lower:upper] += expr   338    339         elif isinstance(augassign.node, compiler.ast.Slice):   340    341             # <lvalue>, <lower>, <upper> -> <lvalue>.__setslice__(<lower>, <upper>, <lvalue>.__getslice__(<lower>, <upper>).__xxx__(expr))   342    343             result.code = [   344                 StoreTemp(   345                     index="expr",   346                     expr=self.dispatch(augassign.node.expr)   347                     ),   348                 StoreTemp(   349                     index="lower",   350                     expr=self.dispatch_or_none(augassign.node.lower)   351                     ),   352                 StoreTemp(   353                     index="upper",   354                     expr=self.dispatch_or_none(augassign.node.upper)   355                     ),   356                 StoreTemp(   357                     expr=InvokeFunction( # referenced below   358                         augassign,   359                         args=[expr], star=None, dstar=None,   360                         expr=LoadAttr(   361                             expr=self._visitSlice(   362                                 augassign.node,   363                                 LoadTemp(index="expr"),   364                                 LoadTemp(index="lower"),   365                                 LoadTemp(index="upper"),   366                                 "OP_APPLY"),   367                             name=self.augassign_methods[augassign.op]   368                             )   369                         )   370                     ),   371                 self._visitSlice(   372                     augassign.node,   373                     LoadTemp(index="expr"),   374                     LoadTemp(index="lower"),   375                     LoadTemp(index="upper"),   376                     "OP_ASSIGN",   377                     LoadTemp()   378                     ),   379                 ReleaseTemp(index="expr"),   380                 ReleaseTemp(index="lower"),   381                 ReleaseTemp(index="upper"),   382                 ReleaseTemp()   383                 ]   384    385             # Make nice annotations for the viewer.   386    387             augassign._op_call = result.code[3].expr   388    389         # Complicated augassign using subscripts: lvalue[subs] += expr   390    391         elif isinstance(augassign.node, compiler.ast.Subscript):   392    393             # <lvalue>, <subs> -> <lvalue>.__setitem__(<subs>, <lvalue>.__getitem__(<subs>).__xxx__(expr))   394    395             result.code = [   396                 StoreTemp(index="expr", expr=self.dispatch(augassign.node.expr)),   397                 StoreTemp(index="subs", expr=self._visitSubscriptSubs(augassign.node, augassign.node.subs)),   398                 StoreTemp(   399                     expr=InvokeFunction( # referenced below   400                         augassign,   401                         args=[expr], star=None, dstar=None,   402                         expr=LoadAttr(   403                             expr=self._visitSubscript(   404                                 augassign.node,   405                                 LoadTemp(index="expr"),   406                                 LoadTemp(index="subs"),   407                                 "OP_APPLY"   408                                 ),   409                             name=self.augassign_methods[augassign.op]   410                             )   411                         )   412                     ),   413                 self._visitSubscript(   414                     augassign.node,   415                     LoadTemp(index="expr"),   416                     LoadTemp(index="subs"),   417                     "OP_ASSIGN",   418                     LoadTemp()   419                     ),   420                 ReleaseTemp(index="expr"),   421                 ReleaseTemp(index="subs"),   422                 ReleaseTemp()   423                 ]   424    425             # Make nice annotations for the viewer.   426    427             augassign._op_call = result.code[2].expr   428    429         else:   430             raise NotImplementedError, augassign.node.__class__   431    432         return result   433    434     def visitBreak(self, break_):   435         result = ReturnFromBlock(break_, 1)   436         return result   437    438     def visitCallFunc(self, callfunc):   439         result = InvokeFunction(callfunc, 1, star=None, dstar=None, args=self.dispatches(callfunc.args))   440         if callfunc.star_args is not None:   441             result.star = self.dispatch(callfunc.star_args)   442         if callfunc.dstar_args is not None:   443             result.dstar = self.dispatch(callfunc.dstar_args)   444         result.expr = self.dispatch(callfunc.node)   445         return result   446    447     def visitClass(self, class_):   448    449         # Add "object" if the class is not "object" and has an empty bases list.   450    451         if class_.name != "object" and not class_.bases:   452             bases = [compiler.ast.Name("object")]   453         else:   454             bases = class_.bases   455    456         structure = Class(name=class_.name, module=self.module, bases=self.dispatches(bases))   457         self.structures.append(structure)   458         within_class = self.within_class   459         self.within_class = 1   460    461         # Make a subprogram which initialises the class structure.   462    463         subprogram = Subprogram(name=None, module=self.module, structure=structure, params=[], star=None, dstar=None)   464         self.current_subprograms.append(subprogram)   465         self.current_structures.append(structure) # mostly for name construction   466    467         # The class is initialised using the code found inside.   468    469         subprogram.code = self.dispatch(class_.code) + [ReturnFromBlock()]   470    471         self.within_class = within_class   472         self.current_structures.pop()   473         self.current_subprograms.pop()   474         self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram   475    476         # Make a definition of the class associating it with a name.   477    478         result = Assign(   479             code=[   480                 StoreName(class_, 1, # defines the class   481                     name=class_.name,   482                     expr=LoadRef(ref=structure)   483                     ),   484                 InvokeBlock(   485                     class_,   486                     share_locals=0, # override the local sharing usually in InvokeBlock   487                     expr=LoadRef(ref=subprogram)   488                     )   489                 ]   490             )   491         return result   492    493     comparison_methods = {   494         "==" : "__eq__", "!=" : "__ne__", "<" : "__lt__", "<=" : "__le__",   495         ">=" : "__ge__", ">" : "__gt__", "is" : None, "is not" : None   496         }   497    498     def visitCompare(self, compare):   499    500         """   501         Make a subprogram for the 'compare' node and record its contents inside   502         the subprogram. Convert...   503    504         Compare (expr)   505                 (name/node)   506                 ...   507    508         ...to:   509    510         InvokeBlock -> Subprogram -> Conditional (test) -> (body)   511                                                  (else) -> Conditional (test) -> (body)   512                                                                        (else) -> ...   513         """   514    515         subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)   516         self.current_subprograms.append(subprogram)   517    518         # In the subprogram, make instructions which invoke a method on the   519         # first operand of each operand pair and, if appropriate, return with   520         # the value from that method.   521    522         last = compare.ops[-1]   523         previous = self.dispatch(compare.expr)   524         results = nodes = []   525    526         # For viewing purposes, record invocations on the AST node.   527    528         compare._ops = []   529    530         for op in compare.ops:   531             op_name, node = op   532             expr = self.dispatch(node)   533    534             # Identify the operation and produce the appropriate method call.   535    536             method_name = self.comparison_methods[op_name]   537             if method_name:   538                 invocation = InvokeFunction(   539                     compare,   540                     expr=LoadAttr(   541                         expr=previous,   542                         name=method_name),   543                     args=[expr],   544                     star=None,   545                     dstar=None)   546    547             elif op_name == "is":   548                 invocation = InvokeFunction(   549                     compare,   550                     expr=LoadName(name="__is__"),   551                     args=[previous, expr],   552                     star=None,   553                     dstar=None)   554    555             elif op_name == "is not":   556                 invocation = Not(   557                     expr=InvokeFunction(   558                         compare,   559                         expr=LoadName(name="__is__"),   560                         args=[previous, expr],   561                         star=None,   562                         dstar=None)   563                     )   564             else:   565                 raise NotImplementedError, op_name   566    567             nodes.append(StoreTemp(expr=invocation))   568             compare._ops.append(invocation)   569    570             # Return from the subprogram where the test is not satisfied.   571    572             if op is not last:   573                 nodes.append(   574                     Conditional(   575                         test=self._visitNot(LoadTemp()),   576                         body=[   577                             ReturnFromBlock(expr=LoadTemp())   578                             ],   579                         else_=[   580                             ReleaseTemp()   581                             # Subsequent operations go here!   582                             ]   583                         )   584                     )   585    586                 # Put subsequent operations in the else section of this conditional.   587    588                 nodes = nodes[-1].else_   589    590             # For the last operation, return the result.   591    592             else:   593                 nodes.append(   594                     ReturnFromBlock(expr=LoadTemp(release=1))   595                     )   596    597             previous = expr   598    599         # Finish the subprogram definition.   600    601         subprogram.code = results   602    603         self.current_subprograms.pop()   604         self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram   605    606         # Make an invocation of the subprogram.   607    608         result = InvokeBlock(compare, 1, produces_result=1)   609         result.expr = LoadRef(ref=subprogram)   610         return result   611    612     def visitConst(self, const):   613         key = "%s-%s" % (const.value.__class__.__name__, const.value)   614         if not self.constants.has_key(key):   615             self.constants[key] = Constant(name=repr(const.value), value=const.value)   616         result = LoadRef(const, 1, ref=self.constants[key])   617         return result   618    619     def visitContinue(self, continue_):   620         result = InvokeBlock(continue_, 1,   621             expr=LoadRef(ref=self.current_subprograms[-1])   622             )   623         return result   624    625     def visitDict(self, dict):   626         result = InvokeFunction(dict, 1, expr=LoadName(name="dict"), star=None, dstar=None)   627         args = []   628         for key, value in dict.items:   629             tuple = InvokeFunction(dict, expr=LoadName(name="tuple"), star=None, dstar=None)   630             tuple.set_args([self.dispatch(key), self.dispatch(value)])   631             args.append(tuple)   632         result.set_args(args)   633         return result   634    635     def visitDiscard(self, discard):   636         return self.dispatch(discard.expr)   637    638     def visitDiv(self, div):   639         return self._visitBinary(div, "__div__", "__rdiv__")   640    641     def visitFloorDiv(self, floordiv):   642         return self._visitBinary(floordiv, "__floordiv__", "__rfloordiv__")   643    644     def visitFor(self, for_):   645    646         """   647         Make a subprogram for the 'for_' node and record its contents inside the   648         subprogram. Convert...   649    650         For (assign)   651             (body)   652             (else)   653    654         ...to:   655    656         Assign (assign #1)   657         Invoke -> Subprogram -> Try (body) -> (assign #2)   658                                               (body)   659                                               Invoke subprogram   660                                     (handler) -> ...   661                                     (else) -> ...   662         """   663    664         subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None)   665         self.current_subprograms.append(subprogram)   666    667         # Always return from conditional sections/subprograms.   668    669         if for_.else_ is not None:   670             else_stmt = self.dispatch(for_.else_) + [ReturnFromBlock()]   671         else:   672             else_stmt = [ReturnFromBlock()]   673    674         # Wrap the assignment in a try...except statement.   675         # Inside the body, add a recursive invocation to the subprogram.   676    677         subprogram.code = [   678             Try(   679                 body=[   680                     Assign(   681                         code=[   682                             StoreTemp(expr=InvokeFunction(for_, expr=LoadAttr(expr=LoadTemp(), name="next"))),   683                             self.dispatch(for_.assign),   684                             ReleaseTemp()   685                             ])   686                     ] + self.dispatch(for_.body) + [   687                     InvokeBlock(   688                         for_,   689                         expr=LoadRef(ref=subprogram)   690                         )   691                     ],   692                 handler=[   693                     Conditional(   694                         test=InvokeFunction(   695                             for_,   696                             expr=LoadName(name="isinstance"),   697                             args=[LoadExc(), LoadName(name="StopIteration")],   698                             star=None,   699                             dstar=None),   700                         body=else_stmt,   701                         else_=[Raise(expr=LoadExc())]   702                         )   703                     ],   704                 else_=[],   705                 finally_=[]   706                 ),   707             ReturnFromBlock()   708             ]   709    710         # Finish the subprogram definition.   711    712         self.current_subprograms.pop()   713         self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram   714    715         # Obtain an iterator for the sequence involved.   716         # Then, make an invocation of the subprogram.   717    718         result = Assign(for_, 1,   719             code=[   720                 StoreTemp(   721                     expr=InvokeFunction(   722                         for_,   723                         expr=LoadAttr(   724                             name="__iter__",   725                             expr=self.dispatch(for_.list)   726                             )   727                         )   728                     ),   729                 InvokeBlock(for_, expr=LoadRef(ref=subprogram)),   730                 ReleaseTemp()   731                 ]   732             )   733    734         # Make nice annotations for the viewer.   735    736         for_._iter_call = result.code[0].expr   737         for_._next_call = subprogram.code[0].body[0].code[0].expr   738    739         return result   740    741     def visitFrom(self, from_):   742         result = Assign(from_, 1)   743         code = []   744         _names = []   745         code.append(   746             StoreTemp(   747                 expr=Import(name=from_.modname, alias=1)   748                 )   749             )   750         from_._modname = code[-1].expr   751         for name, alias in from_.names:   752             code.append(   753                 StoreName(   754                     expr=LoadAttr(   755                         expr=LoadTemp(),   756                         name=name),   757                     name=(alias or name)   758                     )   759                 )   760             _names.append(code[-1].expr)   761         code.append(ReleaseTemp())   762         result.code = code   763         from_._names = _names   764         return result   765    766     def _visitFunction(self, function, subprogram):   767    768         """   769         A common function generator which transforms the given 'function' node   770         and initialises the given 'subprogram' appropriately.   771         """   772    773         # Discover star and dstar parameters.   774    775         if function.flags & 4 != 0:   776             has_star = 1   777         else:   778             has_star = 0   779         if function.flags & 8 != 0:   780             has_dstar = 1   781         else:   782             has_dstar = 0   783    784         # Discover the number of defaults and positional parameters.   785    786         ndefaults = len(function.defaults)   787         npositional = len(function.argnames) - has_star - has_dstar   788    789         # Produce star and dstar parameters with appropriate defaults.   790    791         if has_star:   792             star = (   793                 function.argnames[npositional],   794                 self.dispatch(compiler.ast.List([]))   795                 )   796         else:   797             star = None   798         if has_dstar:   799             dstar = (   800                 function.argnames[npositional + has_star],   801                 self.dispatch(compiler.ast.Dict([]))   802                 )   803         else:   804             dstar = None   805    806         params = []   807         for i in range(0, npositional - ndefaults):   808             params.append((function.argnames[i], None))   809    810         # Process defaults.   811    812         for i in range(0, ndefaults):   813             default = function.defaults[i]   814             if default is not None:   815                 params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default)))   816             else:   817                 params.append((function.argnames[npositional - ndefaults + i], None))   818    819         subprogram.params = params   820         subprogram.star = star   821         subprogram.dstar = dstar   822         self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram   823    824     def visitFunction(self, function):   825    826         """   827         Make a subprogram for the 'function' and record it outside the main   828         tree. Produce something like the following:   829    830         StoreName (name)   831                   (expr) -> LoadRef (ref) -> Subprogram (params)   832                                                         (star)   833                                                         (dstar)   834         """   835    836         subprogram = Subprogram(name=function.name, module=self.module, structures=self.current_structures[:],   837             internal=0, returns_value=1, star=None, dstar=None, is_method=self.within_class, original_def=function)   838    839         self.current_subprograms.append(subprogram)   840         within_class = self.within_class   841         self.within_class = 0   842    843         subprogram.code = self.dispatch(function.code) + [ReturnFromFunction()]   844    845         self.within_class = within_class   846         self.current_subprograms.pop()   847         self._visitFunction(function, subprogram)   848    849         # Make a definition of the function associating it with a name.   850    851         result = StoreName(function, 1, name=function.name, expr=LoadRef(ref=subprogram))   852         return result   853    854     def visitGetattr(self, getattr):   855         result = LoadAttr(getattr, 1,   856             name=getattr.attrname,   857             expr=self.dispatch(getattr.expr)   858             )   859         return result   860    861     def visitGlobal(self, global_):   862         result = Global(global_, 1,   863             names=global_.names   864             )   865         return result   866    867     def visitIf(self, if_):   868    869         """   870         Make conditionals for each test from an 'if_' AST node, adding the body   871         and putting each subsequent test as part of the conditional's else   872         section.   873    874         Convert...   875    876         If (test/body)   877            (test/body)   878            ...   879            (else/body)   880    881         ...to:   882    883         Conditional (test) -> (body)   884                     (else) -> Conditional (test) -> (body)   885                                           (else) -> ...   886         """   887    888    889         results = nodes = []   890    891         # Produce something like...   892         # expr.__bool__() ? body   893    894         first = 1   895         for compare, stmt in if_.tests:   896    897             # Set the first as the defining node.   898    899             test = Conditional(if_, first,   900                 test=InvokeFunction(   901                     if_,   902                     expr=LoadAttr(   903                         expr=self.dispatch(compare),   904                         name="__bool__"   905                         ),   906                     )   907                 )   908             test.body = self.dispatch(stmt)   909             nodes.append(test)   910             nodes = test.else_ = []   911             first = 0   912    913         # Add the compound statement from any else clause to the end.   914    915         if if_.else_ is not None:   916             nodes += self.dispatch(if_.else_)   917    918         result = results[0]   919         return result   920    921     def visitImport(self, import_):   922         result = Assign(import_, 1)   923         code = []   924         _names = []   925         for path, alias in import_.names:   926             importer = Import(name=path, alias=alias)   927             top = alias or path.split(".")[0]   928             code.append(StoreName(expr=importer, name=top))   929             _names.append(code[-1].expr)   930         result.code = code   931         import_._names = _names   932         return result   933    934     def visitInvert(self, invert):   935         return self._visitUnary(invert, "__invert__")   936    937     def visitKeyword(self, keyword):   938         result = Keyword(keyword, 1,   939             name=keyword.name,   940             expr=self.dispatch(keyword.expr)   941             )   942         return result   943    944     def visitLambda(self, lambda_):   945    946         # Make a subprogram for the function and record it outside the main   947         # tree.   948    949         subprogram = Subprogram(name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=lambda_)   950         self.current_subprograms.append(subprogram)   951         subprogram.code = [ReturnFromFunction(expr=self.dispatch(lambda_.code))]   952         self.current_subprograms.pop()   953         self._visitFunction(lambda_, subprogram)   954    955         # Get the subprogram reference to the lambda.   956    957         return LoadRef(lambda_, 1, ref=subprogram)   958    959     def visitList(self, list):   960         return self._visitBuiltin(list, "list")   961    962     def visitMod(self, mod):   963         return self._visitBinary(mod, "__mod__", "__rmod__")   964    965     def visitMul(self, mul):   966         return self._visitBinary(mul, "__mul__", "__rmul__")   967    968     def visitName(self, name):   969         result = LoadName(name, 1, name=name.name)   970         return result   971    972     def _visitNot(self, expr, not_=None):   973         invocation = InvokeFunction(   974             not_, # NOTE: May need a real original node.   975             expr=LoadAttr(   976                 expr=expr,   977                 name="__bool__"   978                 ),   979             )   980         if not_ is not None:   981             result = Not(not_, 1, expr=invocation)   982         else:   983             result = Not(expr=invocation)   984         return result   985    986     def visitNot(self, not_):   987         return self._visitNot(self.dispatch(not_.expr), not_)   988    989     def visitOr(self, or_):   990    991         """   992         Make a subprogram for the 'or_' node and record its contents inside the   993         subprogram. Convert...   994    995         Or (test)   996            (test)   997            ...   998    999         ...to:  1000   1001         Subprogram -> Conditional (test) -> ReturnFromBlock ...  1002                                   (else) -> Conditional (test) -> ReturnFromBlock ...  1003                                                         (else) -> ...  1004         """  1005   1006         subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)  1007         self.current_subprograms.append(subprogram)  1008   1009         # In the subprogram, make instructions which store each operand, test  1010         # for each operand's truth status, and if appropriate return from the  1011         # subprogram with the value of the operand.  1012   1013         last = or_.nodes[-1]  1014         results = nodes = []  1015   1016         for node in or_.nodes:  1017             expr = self.dispatch(node)  1018   1019             # Return from the subprogram where the test is satisfied.  1020   1021             if node is not last:  1022                 nodes.append(StoreTemp(expr=expr))  1023                 invocation = InvokeFunction(or_, expr=LoadAttr(expr=LoadTemp(), name="__bool__"))  1024                 test = Conditional(test=invocation, body=[ReturnFromBlock(expr=LoadTemp())])  1025                 nodes.append(test)  1026   1027                 # Put subsequent operations in the else section of this conditional.  1028   1029                 nodes = test.else_ = [ReleaseTemp()]  1030   1031             # For the last operation, return the result.  1032   1033             else:  1034                 nodes.append(  1035                     ReturnFromBlock(expr=expr)  1036                     )  1037   1038         # Finish the subprogram definition.  1039   1040         subprogram.code = results  1041   1042         self.current_subprograms.pop()  1043         self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram  1044   1045         # Make an invocation of the subprogram.  1046   1047         result = InvokeBlock(or_, 1, produces_result=1)  1048         result.expr = LoadRef(ref=subprogram)  1049         return result  1050   1051     def visitPass(self, pass_):  1052         return Pass(pass_, 1)  1053   1054     def visitPower(self, power):  1055         return self._visitBinary(power, "__pow__", "__rpow__")  1056   1057     def visitPrint(self, print_):  1058   1059         """  1060         Convert...  1061   1062         Print (dest) ->  1063               (nodes)  1064   1065         ...to:  1066   1067         StoreTemp (index) -> "print"  1068                   (expr) -> LoadAttr (expr) -> (dest)  1069                                      (name) -> "write"  1070         InvokeFunction (expr) -> LoadTemp (index) -> "print"  1071                        (args) -> [(node)]  1072         ReleaseTemp (index) -> "print"  1073         """  1074   1075         if print_.dest is not None:  1076             dest = self.dispatch(print_.dest)  1077         else:  1078             dest = self.dispatch(compiler.ast.Name("stdout"))  1079   1080         result = Assign(print_, 1,  1081             code=[  1082                 StoreTemp(  1083                     index="print",  1084                     expr=LoadAttr(  1085                         expr=dest,  1086                         name="write"  1087                         )  1088                     )  1089                 ]  1090             )  1091   1092         for node in print_.nodes:  1093             result.code.append(  1094                 InvokeFunction(  1095                     print_,  1096                     expr=LoadTemp(index="print"),  1097                     args=[self.dispatch(node)],  1098                     star=None,  1099                     dstar=None  1100                     )  1101                 )  1102   1103         result.code.append(  1104             ReleaseTemp(index="print")  1105             )  1106   1107         return result  1108   1109     def visitPrintnl(self, printnl):  1110         result = self.visitPrint(printnl)  1111         result.code.insert(  1112             len(result.code) - 1,  1113             InvokeFunction(  1114                 printnl,  1115                 expr=LoadTemp(index="print"),  1116                 args=[self.dispatch(compiler.ast.Const("\n"))],  1117                 star=None,  1118                 dstar=None  1119                 )  1120             )  1121         return result  1122   1123     def visitRaise(self, raise_):  1124         result = Raise(raise_, 1)  1125         if raise_.expr2 is None:  1126             result.expr = self.dispatch(raise_.expr1)  1127         else:  1128             result.expr = InvokeFunction(  1129                 raise_,  1130                 expr=self.dispatch(raise_.expr1),  1131                 args=[self.dispatch(raise_.expr2)],  1132                 star=None,  1133                 dstar=None  1134                 )  1135         if raise_.expr3 is not None:  1136             result.traceback = self.dispatch(raise_.expr3)  1137         else:  1138             result.traceback = None  1139         return result  1140   1141     def visitReturn(self, return_):  1142         result = ReturnFromFunction(return_, 1,  1143             expr=self.dispatch(return_.value)  1144             )  1145         return result  1146   1147     def _visitSlice(self, slice, expr, lower, upper, flags, value=None):  1148         if flags == "OP_ASSIGN":  1149             result = InvokeFunction(slice, 1,  1150                 expr=LoadAttr(  1151                     expr=expr,  1152                     name="__setslice__"  1153                     ),  1154                 star=None,  1155                 dstar=None,  1156                 args=[lower, upper, value]  1157                 )  1158         elif flags == "OP_APPLY":  1159             args = []  1160             result = InvokeFunction(slice, 1,  1161                 expr=LoadAttr(  1162                     expr=expr,  1163                     name="__getslice__"  1164                     ),  1165                 star=None,  1166                 dstar=None,  1167                 args=[lower, upper]  1168                 )  1169         elif flags == "OP_DELETE":  1170             args = []  1171             result = InvokeFunction(slice, 1,  1172                 expr=LoadAttr(  1173                     expr=expr,  1174                     name="__delslice__"  1175                     ),  1176                 star=None,  1177                 dstar=None,  1178                 args=[lower, upper]  1179                 )  1180         else:  1181             raise NotImplementedError, flags  1182   1183         return result  1184   1185     def visitSlice(self, slice, in_sequence=0):  1186         return self._visitSlice(slice, self.dispatch(slice.expr), self.dispatch_or_none(slice.lower),  1187             self.dispatch_or_none(slice.upper), slice.flags, self._visitAssNameOrAttr(slice, in_sequence))  1188   1189     def visitStmt(self, stmt):  1190         return self.dispatches(stmt.nodes)  1191   1192     def visitSub(self, sub):  1193         return self._visitBinary(sub, "__sub__", "__rsub__")  1194   1195     def _visitSubscript(self, subscript, expr, subs, flags, value=None):  1196         if flags == "OP_ASSIGN":  1197             result = InvokeFunction(subscript, 1,  1198                 expr=LoadAttr(  1199                     expr=expr,  1200                     name="__setitem__"  1201                     ),  1202                 star=None,  1203                 dstar=None,  1204                 args=[subs, value]  1205                 )  1206         elif flags == "OP_APPLY":  1207             args = []  1208             result = InvokeFunction(subscript, 1,  1209                 expr=LoadAttr(  1210                     expr=expr,  1211                     name="__getitem__"  1212                     ),  1213                 star=None,  1214                 dstar=None,  1215                 args=[subs]  1216                 )  1217         elif flags == "OP_DELETE":  1218             args = []  1219             result = InvokeFunction(subscript, 1,  1220                 expr=LoadAttr(  1221                     expr=expr,  1222                     name="__delitem__"  1223                     ),  1224                 star=None,  1225                 dstar=None,  1226                 args=[subs]  1227                 )  1228         else:  1229             raise NotImplementedError, flags  1230   1231         return result  1232   1233     def _visitSubscriptSubs(self, node, subs):  1234         if len(subs) == 1:  1235             return self.dispatch(subs[0])  1236         else:  1237             return InvokeFunction(node, 1,  1238                 expr=LoadName(name="tuple"),  1239                 args=self.dispatches(subs),  1240                 star=None,  1241                 dstar=None  1242                 )  1243   1244     def visitSubscript(self, subscript, in_sequence=0):  1245         return self._visitSubscript(  1246             subscript, self.dispatch(subscript.expr), self._visitSubscriptSubs(subscript, subscript.subs), subscript.flags,  1247             self._visitAssNameOrAttr(subscript, in_sequence)  1248             )  1249   1250     def visitTryExcept(self, tryexcept):  1251   1252         """  1253         Make conditionals for each handler associated with a 'tryexcept' node.  1254   1255         Convert...  1256   1257         TryExcept (body)  1258                   (else)  1259                   (spec/assign/stmt)  1260                   ...  1261   1262         ...to:  1263   1264         Try (body)  1265             (else)  1266             (handler) -> Conditional (test) -> (stmt)  1267                                      (body) -> ...  1268                                      (else) -> Conditional (test) -> (stmt)  1269                                                            (body) -> ...  1270                                                            (else) -> ...  1271         """  1272                     1273         result = Try(tryexcept, 1, body=[], else_=[], finally_=[])  1274   1275         if tryexcept.body is not None:  1276             result.body = self.dispatch(tryexcept.body)  1277         if tryexcept.else_ is not None:  1278             result.else_ = self.dispatch(tryexcept.else_)  1279   1280         results = nodes = []  1281         catch_all = 0  1282   1283         for spec, assign, stmt in tryexcept.handlers:  1284   1285             # If no specification exists, produce an unconditional block.  1286   1287             if spec is None:  1288                 nodes += self.dispatch(stmt)  1289                 catch_all = 1  1290   1291             # Produce an exception value check.  1292   1293             else:  1294                 test = Conditional(  1295                     isolate_test=1,  1296                     test=CheckExc(expr=LoadExc(), choices=self._visitTryExcept(spec))  1297                     )  1298                 test.body = []  1299   1300                 if assign is not None:  1301                     test.body.append(  1302                         Assign(  1303                             code=[  1304                                 StoreTemp(expr=LoadExc()),  1305                                 self.dispatch(assign),  1306                                 ReleaseTemp()  1307                                 ]  1308                             )  1309                         )  1310   1311                 test.body += self.dispatch(stmt)  1312                 nodes.append(test)  1313                 nodes = test.else_ = []  1314   1315         # Add a raise operation to deal with unhandled exceptions.  1316   1317         if not catch_all:  1318             nodes.append(  1319                 Raise(  1320                     expr=LoadExc())  1321                 )  1322   1323         result.handler = results  1324         return result  1325   1326     def _visitTryExcept(self, spec):  1327   1328         "Return a list of nodes for the given exception type 'spec'."  1329   1330         if isinstance(spec, compiler.ast.Tuple):  1331             nodes = []  1332             for node in spec.nodes:  1333                 nodes += self._visitTryExcept(node)  1334         else:  1335             nodes = [self.dispatch(spec)]  1336         return nodes  1337   1338     def visitTryFinally(self, tryfinally):  1339         result = Try(tryfinally, 1, body=[], else_=[], finally_=[])  1340         if tryfinally.body is not None:  1341             result.body = self.dispatch(tryfinally.body)  1342         if tryfinally.final is not None:  1343             result.finally_ = self.dispatch(tryfinally.final)  1344         return result  1345   1346     def visitTuple(self, tuple):  1347         return self._visitBuiltin(tuple, "tuple")  1348   1349     def visitUnaryAdd(self, unaryadd):  1350         return self._visitUnary(unaryadd, "__pos__")  1351   1352     def visitUnarySub(self, unarysub):  1353         return self._visitUnary(unarysub, "__neg__")  1354   1355     def visitWhile(self, while_):  1356   1357         """  1358         Make a subprogram for the 'while' node and record its contents inside the  1359         subprogram. Convert...  1360   1361         While (test) -> (body)  1362               (else)  1363   1364         ...to:  1365   1366         Subprogram -> Conditional (test) -> (body) -> Invoke subprogram  1367                                   (else) -> Conditional (test) -> ReturnFromBlock ...  1368                                                         (else) -> ...  1369         """  1370   1371         subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None)  1372         self.current_subprograms.append(subprogram)  1373   1374         # Include a conditional statement in the subprogram.  1375         # Inside the conditional, add a recursive invocation to the subprogram  1376         # if the test condition was satisfied.  1377         # Return within the main section of the loop.  1378   1379         test = Conditional(  1380             test=InvokeFunction(  1381                 while_,  1382                 expr=LoadAttr(  1383                     expr=self.dispatch(while_.test),  1384                     name="__bool__"),  1385                 ),  1386             body=self.dispatch(while_.body) + [  1387                 InvokeBlock(  1388                     while_,  1389                     expr=LoadRef(ref=subprogram)  1390                     ),  1391                 ReturnFromBlock()  1392                 ],  1393             else_=[]  1394             )  1395   1396         # Provide the else section, if present, along with an explicit return.  1397   1398         if while_.else_ is not None:  1399             test.else_ = self.dispatch(while_.else_) + [ReturnFromBlock()]  1400   1401         # Finish the subprogram definition.  1402   1403         subprogram.code = [test]  1404   1405         self.current_subprograms.pop()  1406         self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram  1407   1408         # Make an invocation of the subprogram.  1409   1410         result = InvokeBlock(while_, 1,  1411             expr=LoadRef(ref=subprogram)  1412             )  1413   1414         # Make nice annotations for the viewer.  1415   1416         while_._test_call = subprogram.code[0].test  1417   1418         return result  1419   1420     # Convenience methods.  1421   1422     def _visitBinary(self, binary, left_name, right_name):  1423   1424         """  1425         Emulate the current mechanisms by producing nodes as follows:  1426   1427         InvokeBlock -> Subprogram -> Try (body) -> ReturnFromBlock (expr) -> x.__add__(y)  1428                                          (else)  1429                                          (handler) -> Conditional (test) -> CheckExc (expr) -> LoadExc  1430                                                                                      (choices) -> LoadName TypeError  1431                                                                   (body) -> ReturnFromBlock (expr) -> y.__radd__(x)  1432                                                                   (else)  1433         """  1434   1435         subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None)  1436         self.current_subprograms.append(subprogram)  1437   1438         subprogram.code = [  1439             Try(binary, 1,  1440                 body=[  1441                     ReturnFromBlock(  1442                         expr=InvokeFunction(  1443                             binary,  1444                             expr=LoadAttr(expr=self.dispatch(binary.left), name=left_name),  1445                             args=[self.dispatch(binary.right)],  1446                             star=None,  1447                             dstar=None)  1448                         )  1449                     ],  1450                 else_=[],  1451                 finally_=[],  1452                 handler=[  1453                     Conditional(  1454                         test=CheckExc(expr=LoadExc(), choices=[LoadName(name="TypeError")]),  1455                         body=[  1456                             ReturnFromBlock(  1457                                 expr=InvokeFunction(  1458                                     binary,  1459                                     expr=LoadAttr(expr=self.dispatch(binary.right), name=right_name),  1460                                     args=[self.dispatch(binary.left)],  1461                                     star=None,  1462                                     dstar=None)  1463                                 )  1464                             ],  1465                         else_=[]  1466                         )  1467                     ]  1468                 )  1469             ]  1470   1471         self.current_subprograms.pop()  1472         self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram  1473   1474         result = InvokeBlock(  1475             binary,  1476             produces_result=1,  1477             expr=LoadRef(ref=subprogram)  1478             )  1479   1480         # Make nice annotations for the viewer.  1481   1482         binary._left_call = subprogram.code[0].body[0].expr  1483         binary._right_call = subprogram.code[0].handler[0].body[0].expr  1484   1485         return result  1486   1487     def _visitBuiltin(self, builtin, name):  1488         result = InvokeFunction(builtin, 1, expr=LoadName(name=name), args=self.dispatches(builtin.nodes), star=None, dstar=None)  1489         return result  1490   1491     def _visitUnary(self, unary, name):  1492         result = InvokeFunction(unary, 1,  1493             expr=LoadAttr(  1494                 expr=self.dispatch(unary.expr),  1495                 name=name  1496                 )  1497             )  1498   1499         # Make nice annotations for the viewer.  1500   1501         unary._unary_call = result  1502   1503         return result  1504   1505 # Convenience functions.  1506   1507 def simplify(filename, builtins=0, module_name=None):  1508   1509     """  1510     Simplify the module stored in the file with the given 'filename'.  1511   1512     If the optional 'builtins' parameter is set to a true value (the default  1513     being a false value), then the module is considered as the builtins module.  1514     """  1515   1516     simplifier = Simplifier(builtins)  1517     module = compiler.parseFile(filename)  1518     compiler.misc.set_filename(filename, module)  1519     if builtins:  1520         name = module_name or "__builtins__"  1521     else:  1522         path, ext = os.path.splitext(filename)  1523         path, name = os.path.split(path)  1524         name = module_name or name  1525     simplified = simplifier.process(module, name)  1526     return simplified  1527   1528 # vim: tabstop=4 expandtab shiftwidth=4