simplify

Annotated simplify.py

14:7c40ee4eea88
2006-07-15 paulb Split up the original test.
paul@0 1
#!/usr/bin/env python
paul@0 2
paul@0 3
"""
paul@0 4
Simplify AST structures for easier type propagation and analysis.
paul@0 5
paul@0 6
Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk>
paul@0 7
paul@0 8
This software is free software; you can redistribute it and/or
paul@0 9
modify it under the terms of the GNU General Public License as
paul@0 10
published by the Free Software Foundation; either version 2 of
paul@0 11
the License, or (at your option) any later version.
paul@0 12
paul@0 13
This software is distributed in the hope that it will be useful,
paul@0 14
but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@0 15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@0 16
GNU General Public License for more details.
paul@0 17
paul@0 18
You should have received a copy of the GNU General Public
paul@0 19
License along with this library; see the file LICENCE.txt
paul@0 20
If not, write to the Free Software Foundation, Inc.,
paul@0 21
51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
paul@0 22
"""
paul@0 23
paul@0 24
from compiler.visitor import ASTVisitor
paul@0 25
import compiler.ast
paul@0 26
from simplified import *
paul@0 27
paul@0 28
class Simplifier(ASTVisitor):
paul@0 29
paul@0 30
    """
paul@0 31
    A simplifying visitor for AST nodes.
paul@0 32
paul@5 33
    Covered: And, AssAttr, AssList, AssName, AssTuple, Assign, AugAssign, Break,
paulb@13 34
             CallFunc, Class, Compare, Const, Continue, Dict, Discard, For,
paulb@13 35
             From, Function, Getattr, Global, If, Import, Invert, Keyword,
paulb@13 36
             Lambda, List, Module, Name, Not, Or, Pass, Raise, Return, Slice,
paulb@13 37
             Stmt, Subscript, TryExcept, TryFinally, Tuple, While, UnaryAdd,
paulb@13 38
             UnarySub.
paul@0 39
paulb@13 40
    Missing: Add, Assert, Backquote, Bitand, Bitor, Bitxor, Decorators, Div,
paulb@13 41
             Ellipsis, Exec, FloorDiv, LeftShift, ListComp, ListCompFor,
paulb@13 42
             ListCompIf, Mod, Mul, Power, Print, Printnl, RightShift, Sliceobj,
paulb@13 43
             Sub, Yield.
paul@0 44
    """
paul@0 45
paul@0 46
    def __init__(self):
paul@0 47
        ASTVisitor.__init__(self)
paul@1 48
        self.result = None              # The resulting tree.
paul@1 49
        self.subprograms = []           # Subprograms outside the tree.
paul@1 50
        self.current_subprograms = []   # Current subprograms being processed.
paul@0 51
paul@0 52
    # Generic visitor methods.
paul@0 53
paul@0 54
    def default(self, node, *args):
paul@0 55
        raise ValueError, node.__class__
paul@0 56
paul@0 57
    def dispatch(self, node, *args):
paul@0 58
        return ASTVisitor.dispatch(self, node, *args)
paul@0 59
paul@0 60
    def dispatches(self, nodes, *args):
paul@0 61
        results = []
paul@0 62
        for node in nodes:
paul@0 63
            results.append(self.dispatch(node, *args))
paul@0 64
        return results
paul@0 65
paulb@6 66
    def dispatch_or_none(self, node, *args):
paulb@6 67
        if node is not None:
paulb@6 68
            return self.dispatch(node, *args)
paulb@6 69
        else:
paulb@6 70
            return LoadName(name="None")
paulb@6 71
paul@0 72
    # Placeholder or deletion transformations.
paul@0 73
paul@0 74
    def visitStmt(self, stmt):
paul@0 75
        return self.dispatches(stmt.nodes)
paul@0 76
paul@0 77
    def visitPass(self, pass_):
paul@0 78
        return Pass(pass_)
paul@0 79
paul@0 80
    def visitDiscard(self, discard):
paul@0 81
        return self.dispatch(discard.expr)
paul@0 82
paul@0 83
    # Relatively trivial transformations.
paul@0 84
paul@0 85
    def visitModule(self, module):
paul@0 86
        self.result = Module(module)
paul@0 87
        self.result.code = self.dispatch(module.node)
paul@0 88
        return self.result
paul@0 89
paul@0 90
    def visitClass(self, class_):
paul@0 91
        result = Class(class_, name=class_.name, bases=class_.bases)
paul@0 92
        result.code = self.dispatch(class_.code)
paul@0 93
        return result
paul@0 94
paul@0 95
    def visitGetattr(self, getattr):
paul@0 96
        result = LoadAttr(getattr, name=getattr.attrname)
paul@0 97
        result.expr = self.dispatch(getattr.expr)
paul@0 98
        return result
paul@0 99
paul@0 100
    def visitKeyword(self, keyword):
paul@0 101
        result = Keyword(keyword, name=keyword.name)
paul@0 102
        result.expr = self.dispatch(keyword.expr)
paul@0 103
        return result
paul@0 104
paul@1 105
    def visitGlobal(self, global_):
paul@1 106
        result = Global(global_, names=global_.names)
paul@1 107
        return result
paul@1 108
paul@3 109
    def visitImport(self, import_):
paul@3 110
        result = Assign(import_)
paul@3 111
        code = []
paul@3 112
        for path, alias in import_.names:
paul@3 113
            importer = Import(name=path)
paul@3 114
            top = alias or path.split(".")[0]
paul@3 115
            code.append(StoreName(expr=importer, name=top))
paul@3 116
        result.code = code
paul@3 117
        return result
paul@3 118
paul@3 119
    def visitFrom(self, from_):
paul@3 120
        result = Assign(from_)
paul@3 121
        code = []
paul@3 122
        code.append(StoreTemp(expr=Import(name=from_.modname)))
paul@3 123
        for name, alias in from_.names:
paul@3 124
            code.append(StoreName(expr=LoadAttr(expr=LoadTemp(), name=name), name=(alias or name)))
paul@3 125
        result.code = code
paul@3 126
        return result
paul@3 127
paul@0 128
    def visitName(self, name):
paul@0 129
        result = LoadName(name, name=name.name)
paul@0 130
        return result
paul@0 131
paul@0 132
    def visitConst(self, const):
paul@0 133
        result = LoadConst(const, value=const.value)
paul@0 134
        return result
paul@0 135
paul@0 136
    def visitReturn(self, return_):
paul@0 137
        result = Return(return_)
paul@0 138
        result.expr = self.dispatch(return_.value)
paul@0 139
        return result
paul@0 140
paul@0 141
    def visitBreak(self, break_):
paul@0 142
        result = Return(break_)
paul@0 143
        return result
paul@0 144
paul@0 145
    def visitContinue(self, continue_):
paulb@7 146
        result = Invoke(continue_, same_frame=1, produces_result=0, star=None, dstar=None, args=[])
paul@0 147
        result.expr = LoadRef(ref=self.current_subprograms[-1])
paul@0 148
        return result
paul@0 149
paul@5 150
    def visitRaise(self, raise_):
paul@5 151
        result = Raise(raise_, expr=self.dispatch(raise_.expr1), traceback=None)
paul@5 152
        if raise_.expr2 is not None:
paul@5 153
            result.args = [self.dispatch(raise_.expr2)]
paul@5 154
        if raise_.expr3 is not None:
paul@5 155
            result.traceback = self.dispatch(raise_.expr3)
paul@5 156
        return result
paul@5 157
paul@0 158
    def visitIf(self, if_):
paul@0 159
        result = If(if_, else_=[])
paul@0 160
        tests = []
paul@0 161
        for compare, stmt in if_.tests:
paul@1 162
            # Produce something like...
paul@1 163
            # expr.__true__() ? body
paul@0 164
            test = Conditional(else_=[], test=Invoke(
paul@0 165
                expr=LoadAttr(expr=self.dispatch(compare), name="__true__"),
paul@0 166
                params=[], star=None, dstar=None))
paul@0 167
            test.body = self.dispatch(stmt)
paul@0 168
            tests.append(test)
paul@0 169
        result.tests = tests
paul@0 170
        if if_.else_ is not None:
paul@0 171
            result.else_ = self.dispatch(if_.else_)
paul@0 172
        return result
paul@0 173
paul@0 174
    def _visitBuiltin(self, builtin, name):
paul@1 175
        result = Invoke(builtin, expr=LoadName(name=name))
paul@1 176
        result.args = self.dispatches(builtin.nodes)
paul@0 177
        return result
paul@0 178
paul@0 179
    def visitTuple(self, tuple):
paul@0 180
        return self._visitBuiltin(tuple, "Tuple")
paul@0 181
paul@0 182
    def visitList(self, list):
paul@0 183
        return self._visitBuiltin(list, "List")
paul@0 184
paul@0 185
    def visitDict(self, dict):
paulb@11 186
        result = Invoke(dict, expr=LoadName(name="Dict"))
paul@0 187
        args = []
paul@0 188
        for key, value in dict.items:
paul@0 189
            tuple = Invoke(expr=LoadName(name="Tuple"), star=None, dstar=None)
paul@0 190
            tuple.args = [self.dispatch(key), self.dispatch(value)]
paul@0 191
            args.append(tuple)
paul@0 192
        result.args = args
paul@0 193
        return result
paul@0 194
paulb@13 195
    # Logical and comparison operations.
paulb@13 196
paulb@13 197
    comparison_methods = {
paulb@13 198
        "==" : "__eq__", "!=" : "__ne__", "<" : "__lt__", "<=" : "__le__",
paulb@13 199
        ">=" : "__ge__", ">" : "__gt__", "is" : None, "is not" : None
paulb@13 200
        }
paulb@13 201
paulb@13 202
    def visitCompare(self, compare):
paulb@13 203
paulb@13 204
        # Make a subprogram for the expression and record it outside the main tree.
paulb@13 205
paulb@13 206
        subprogram = Subprogram(name=hex(id(compare)), acquire_locals=1, returns_value=1, params=[], star=None, dstar=None)
paulb@13 207
        self.current_subprograms.append(subprogram)
paulb@13 208
paulb@13 209
        # In the subprogram, make instructions which invoke a method on the
paulb@13 210
        # first operand of each operand pair and, if appropriate, return with
paulb@13 211
        # the value from that method.
paulb@13 212
paulb@13 213
        nodes = []
paulb@13 214
        last = compare.ops[-1]
paulb@13 215
        previous = self.dispatch(compare.expr)
paulb@13 216
        for op in compare.ops:
paulb@13 217
            op_name, node = op
paulb@13 218
            expr = self.dispatch(node)
paulb@13 219
            method_name = self.comparison_methods[op_name]
paulb@13 220
            if method_name:
paulb@13 221
                invocation = Invoke(expr=LoadAttr(expr=previous, name=method_name), params=[expr], star=None, dstar=None)
paulb@13 222
            elif op_name == "is":
paulb@13 223
                invocation = Invoke(expr=LoadName(name="__is__"), params=[previous, expr], star=None, dstar=None)
paulb@13 224
            elif op_name == "is not":
paulb@13 225
                invocation = Not(expr=Invoke(expr=LoadName(name="__is__"), params=[previous, expr], star=None, dstar=None))
paulb@13 226
            else:
paulb@13 227
                raise NotImplementedError, op_name
paulb@13 228
            nodes.append(StoreTemp(expr=invocation))
paulb@13 229
            if op is not last:
paulb@13 230
                nodes.append(Conditional(test=Not(expr=LoadTemp()), body=[Return(expr=LoadTemp())]))
paulb@13 231
                nodes.append(ReleaseTemp())
paulb@13 232
            else:
paulb@13 233
                nodes.append(Return(expr=LoadTemp()))
paulb@13 234
        subprogram.code = nodes
paulb@13 235
paulb@13 236
        self.current_subprograms.pop()
paulb@13 237
        self.subprograms.append(subprogram)
paulb@13 238
paulb@13 239
        # Make an invocation of the subprogram.
paulb@13 240
paulb@13 241
        result = Invoke(compare, same_frame=1, star=None, dstar=None, args=[])
paulb@13 242
        result.expr = LoadRef(ref=subprogram)
paulb@13 243
        return result
paul@1 244
paul@1 245
    def visitAnd(self, and_):
paulb@7 246
paulb@7 247
        # Make a subprogram for the expression and record it outside the main tree.
paulb@7 248
paulb@11 249
        subprogram = Subprogram(name=hex(id(and_)), acquire_locals=1, returns_value=1, params=[], star=None, dstar=None)
paulb@7 250
        self.current_subprograms.append(subprogram)
paulb@7 251
paulb@12 252
        # In the subprogram, make instructions which store each operand, test
paulb@12 253
        # for each operand's truth status, and if appropriate return from the
paulb@12 254
        # subprogram with the value of the operand.
paulb@12 255
paul@1 256
        nodes = []
paulb@7 257
        last = and_.nodes[-1]
paul@1 258
        for node in and_.nodes:
paulb@7 259
            expr = self.dispatch(node)
paulb@7 260
            if node is not last:
paulb@12 261
                nodes.append(StoreTemp(expr=expr))
paulb@13 262
                invocation = Invoke(expr=LoadAttr(expr=LoadTemp(), name="__true__"), params=[], star=None, dstar=None)
paulb@13 263
                nodes.append(Conditional(test=Not(expr=invocation), body=[Return(expr=LoadTemp())]))
paulb@7 264
                nodes.append(ReleaseTemp())
paulb@7 265
            else:
paulb@7 266
                nodes.append(Return(expr=expr))
paulb@7 267
        subprogram.code = nodes
paulb@7 268
paulb@7 269
        self.current_subprograms.pop()
paulb@7 270
        self.subprograms.append(subprogram)
paulb@7 271
paulb@7 272
        # Make an invocation of the subprogram.
paulb@7 273
paulb@7 274
        result = Invoke(and_, same_frame=1, star=None, dstar=None, args=[])
paulb@7 275
        result.expr = LoadRef(ref=subprogram)
paul@1 276
        return result
paul@1 277
paul@1 278
    def visitOr(self, or_):
paulb@7 279
paulb@7 280
        # Make a subprogram for the expression and record it outside the main tree.
paulb@7 281
paulb@11 282
        subprogram = Subprogram(name=hex(id(or_)), acquire_locals=1, returns_value=1, params=[], star=None, dstar=None)
paulb@7 283
        self.current_subprograms.append(subprogram)
paulb@7 284
paulb@12 285
        # In the subprogram, make instructions which store each operand, test
paulb@12 286
        # for each operand's truth status, and if appropriate return from the
paulb@12 287
        # subprogram with the value of the operand.
paulb@12 288
paul@1 289
        nodes = []
paulb@7 290
        last = or_.nodes[-1]
paul@1 291
        for node in or_.nodes:
paulb@7 292
            expr = self.dispatch(node)
paulb@7 293
            if node is not last:
paulb@12 294
                nodes.append(StoreTemp(expr=expr))
paulb@12 295
                invocation = Invoke(expr=LoadAttr(expr=LoadTemp(), name="__true__"),
paulb@7 296
                    params=[], star=None, dstar=None)
paulb@7 297
                nodes.append(Conditional(test=invocation, body=[Return(expr=LoadTemp())]))
paulb@7 298
                nodes.append(ReleaseTemp())
paulb@7 299
            else:
paulb@7 300
                nodes.append(Return(expr=expr))
paulb@7 301
        subprogram.code = nodes
paulb@7 302
paulb@7 303
        self.current_subprograms.pop()
paulb@7 304
        self.subprograms.append(subprogram)
paulb@7 305
paulb@7 306
        # Make an invocation of the subprogram.
paulb@7 307
paulb@7 308
        result = Invoke(or_, same_frame=1, star=None, dstar=None, args=[])
paulb@7 309
        result.expr = LoadRef(ref=subprogram)
paul@1 310
        return result
paul@1 311
paul@1 312
    def visitNot(self, not_):
paul@1 313
        result = Not(not_, expr=Invoke(expr=LoadAttr(expr=self.dispatch(not_.expr), name="__true__"),
paul@1 314
            params=[], star=None, dstar=None))
paul@1 315
        return result
paul@1 316
paul@4 317
    # Operators.
paul@4 318
paul@4 319
    def visitUnaryAdd(self, unaryadd):
paulb@11 320
        return Invoke(unaryadd, expr=LoadAttr(expr=self.dispatch(unaryadd.expr), name="__pos__"), args=[])
paul@4 321
paul@4 322
    def visitUnarySub(self, unarysub):
paulb@11 323
        return Invoke(unarysub, expr=LoadAttr(expr=self.dispatch(unarysub.expr), name="__neg__"), args=[])
paul@4 324
paul@4 325
    def visitInvert(self, invert):
paulb@11 326
        return Invoke(invert, expr=LoadAttr(expr=self.dispatch(invert.expr), name="__invert__"), args=[])
paul@4 327
paul@0 328
    # Assignments.
paul@0 329
paul@2 330
    augassign_methods = {
paulb@9 331
        "+=" : "__iadd__", "-=" : "__isub__", "*=" : "__imul__", "/=" : "__idiv__",
paulb@9 332
        "%=" : "__imod__", "**=" : "__ipow__", "<<=" : "__ilshift__", ">>=" : "__irshift__",
paulb@9 333
        "&=" : "__iand__", "^=" : "__ixor__", "|=" : "__ior__"
paul@2 334
        }
paul@2 335
paul@2 336
    def visitAugAssign(self, augassign):
paul@2 337
        result = Assign(augassign)
paul@2 338
        expr = self.dispatch(augassign.expr)
paul@2 339
paul@2 340
        # Simple augmented assignment: name += expr
paul@2 341
paul@2 342
        if isinstance(augassign.node, compiler.ast.Name):
paulb@6 343
            name = augassign.node
paulb@6 344
            node = self.dispatch(name)
paul@2 345
            get_incremented = StoreTemp(
paul@2 346
                expr=Invoke(expr=LoadAttr(expr=node, name=self.augassign_methods[augassign.op]), args=[expr])
paul@2 347
                )
paulb@6 348
            store = StoreName(expr=LoadTemp(), name=name.name)
paul@2 349
            result.code = [get_incremented, store, ReleaseTemp()]
paul@2 350
paulb@9 351
        # Complicated augmented assignment: lvalue.attr += expr
paul@2 352
paul@2 353
        elif isinstance(augassign.node, compiler.ast.Getattr):
paulb@6 354
paulb@9 355
            # <lvalue> -> setattr(<lvalue>, getattr(<lvalue>, "attr").__xxx__(expr))
paulb@6 356
paulb@6 357
            getattr = augassign.node
paulb@6 358
            store_expr = StoreTemp(index="expr", expr=self.dispatch(getattr.expr))
paulb@6 359
            node_attr = LoadAttr(expr=LoadTemp(index="expr"), name=getattr.attrname)
paul@2 360
            get_incremented = StoreTemp(
paul@2 361
                expr=Invoke(expr=LoadAttr(expr=node_attr, name=self.augassign_methods[augassign.op]), args=[expr])
paul@2 362
                )
paulb@6 363
            store = StoreAttr(expr=LoadTemp(), lvalue=LoadTemp(index="expr"), name=getattr.attrname)
paulb@6 364
            result.code = [store_expr, get_incremented, store, ReleaseTemp(index="expr"), ReleaseTemp()]
paulb@6 365
paulb@9 366
        # Complicated augassign using slices: lvalue[lower:upper] += expr
paulb@6 367
paulb@6 368
        elif isinstance(augassign.node, compiler.ast.Slice):
paulb@9 369
paulb@9 370
            # <lvalue>, <lower>, <upper> -> <lvalue>.__setslice__(<lower>, <upper>, <lvalue>.__getslice__(<lower>, <upper>).__xxx__(expr))
paulb@9 371
paulb@6 372
            slice = augassign.node
paulb@6 373
            store_expr = StoreTemp(index="expr", expr=self.dispatch(slice.expr))
paulb@6 374
            store_lower = StoreTemp(index="lower", expr=self.dispatch_or_none(slice.lower))
paulb@6 375
            store_upper = StoreTemp(index="upper", expr=self.dispatch_or_none(slice.upper))
paulb@11 376
            node_slice = self._visitSlice(slice, LoadTemp(index="expr"), LoadTemp(index="lower"), LoadTemp(index="upper"), "OP_APPLY")
paulb@6 377
            get_incremented = StoreTemp(
paulb@6 378
                expr=Invoke(expr=LoadAttr(expr=node_slice, name=self.augassign_methods[augassign.op]), args=[expr])
paulb@6 379
                )
paulb@11 380
            store = self._visitSlice(slice, LoadTemp(index="expr"), LoadTemp(index="lower"), LoadTemp(index="upper"), "OP_ASSIGN", LoadTemp())
paulb@6 381
            result.code = [store_expr, store_lower, store_upper, get_incremented, store,
paulb@6 382
                ReleaseTemp(index="expr"), ReleaseTemp(index="lower"), ReleaseTemp(index="upper"), ReleaseTemp()]
paul@2 383
paulb@10 384
        # Complicated augassign using subscripts: lvalue[subs] += expr
paulb@10 385
paulb@10 386
        elif isinstance(augassign.node, compiler.ast.Subscript):
paulb@10 387
paulb@10 388
            # <lvalue>, <subs> -> <lvalue>.__setitem__(<subs>, <lvalue>.__getitem__(<subs>).__xxx__(expr))
paulb@10 389
paulb@10 390
            subscript = augassign.node
paulb@10 391
            store_expr = StoreTemp(index="expr", expr=self.dispatch(subscript.expr))
paulb@10 392
            subs = self._visitSubscriptSubs(subscript.subs)
paulb@10 393
            store_subs = StoreTemp(index="subs", expr=subs)
paulb@11 394
            node_subscript = self._visitSubscript(subscript, LoadTemp(index="expr"), LoadTemp(index="subs"), "OP_APPLY")
paulb@10 395
            get_incremented = StoreTemp(
paulb@10 396
                expr=Invoke(expr=LoadAttr(expr=node_subscript, name=self.augassign_methods[augassign.op]), args=[expr])
paulb@10 397
                )
paulb@11 398
            store = self._visitSubscript(subscript, LoadTemp(index="expr"), LoadTemp(index="subs"), "OP_ASSIGN", LoadTemp())
paulb@10 399
            result.code = [store_expr, store_subs, get_incremented, store, ReleaseTemp(index="expr"), ReleaseTemp(index="subs"), ReleaseTemp()]
paulb@10 400
paul@2 401
        else:
paul@2 402
            raise NotImplementedError, augassign.node.__class__
paul@2 403
paul@2 404
        return result
paul@2 405
paul@0 406
    def visitAssign(self, assign):
paul@0 407
        result = Assign(assign)
paul@0 408
        store = StoreTemp(expr=self.dispatch(assign.expr))
paul@0 409
        release = ReleaseTemp()
paul@0 410
        result.code = [store] + self.dispatches(assign.nodes, 0) + [release]
paul@0 411
        return result
paul@0 412
paul@0 413
    def visitAssList(self, asslist, in_sequence=0):
paul@0 414
        if not in_sequence:
paulb@11 415
            expr = LoadTemp()
paul@0 416
        else:
paulb@11 417
            expr = Invoke(expr=LoadAttr(expr=LoadTemp(), name="next"))
paul@0 418
        result = Assign(asslist)
paul@0 419
        store = StoreTemp(expr=Invoke(expr=LoadAttr(name="__iter__", expr=expr)))
paul@0 420
        release = ReleaseTemp()
paul@0 421
        result.code = [store] + self.dispatches(asslist.nodes, 1) + [release]
paul@0 422
        return result
paul@0 423
paul@0 424
    visitAssTuple = visitAssList
paul@0 425
paul@0 426
    def _visitAssNameOrAttr(self, node, in_sequence):
paul@0 427
        if not in_sequence:
paulb@11 428
            return LoadTemp()
paul@0 429
        else:
paulb@11 430
            return Invoke(expr=LoadAttr(expr=LoadTemp(), name="next"))
paul@0 431
paul@0 432
    def visitAssName(self, assname, in_sequence=0):
paul@0 433
        expr = self._visitAssNameOrAttr(assname, in_sequence)
paul@0 434
        result = StoreName(assname, name=assname.name, expr=expr)
paul@0 435
        return result
paul@0 436
paul@0 437
    def visitAssAttr(self, assattr, in_sequence=0):
paul@0 438
        expr = self._visitAssNameOrAttr(assattr, in_sequence)
paul@0 439
        lvalue = self.dispatch(assattr.expr)
paul@0 440
        result = StoreAttr(assattr, name=assattr.attrname, lvalue=lvalue, expr=expr)
paul@0 441
        return result
paul@0 442
paulb@11 443
    def _visitSlice(self, slice, expr, lower, upper, flags, value=None):
paulb@6 444
        if flags == "OP_ASSIGN":
paulb@6 445
            args = [value]
paulb@6 446
            result = Invoke(expr=LoadAttr(expr=expr, name="__setslice__"))
paulb@6 447
        elif flags == "OP_APPLY":
paulb@6 448
            args = []
paulb@6 449
            result = Invoke(expr=LoadAttr(expr=expr, name="__getslice__"))
paulb@10 450
        elif flags == "OP_DELETE":
paulb@10 451
            args = []
paulb@10 452
            result = Invoke(expr=LoadAttr(expr=expr, name="__delslice__"))
paulb@6 453
        else:
paulb@6 454
            raise NotImplementedError, flags
paulb@6 455
paulb@6 456
        # Add the dimensions.
paulb@6 457
paulb@6 458
        args.insert(0, lower)
paulb@6 459
        args.insert(1, upper)
paulb@6 460
paulb@11 461
        result.original = slice
paulb@6 462
        result.args = args
paulb@6 463
        return result
paulb@6 464
paulb@6 465
    def visitSlice(self, slice, in_sequence=0):
paulb@6 466
        value = self._visitAssNameOrAttr(slice, in_sequence)
paulb@11 467
        return self._visitSlice(slice, self.dispatch(slice.expr), self.dispatch_or_none(slice.lower),
paulb@11 468
            self.dispatch_or_none(slice.upper), slice.flags, value)
paulb@6 469
paulb@11 470
    def _visitSubscript(self, subscript, expr, subs, flags, value=None):
paulb@10 471
        if flags == "OP_ASSIGN":
paulb@10 472
            args = [value]
paulb@10 473
            result = Invoke(expr=LoadAttr(expr=expr, name="__setitem__"))
paulb@10 474
        elif flags == "OP_APPLY":
paulb@10 475
            args = []
paulb@10 476
            result = Invoke(expr=LoadAttr(expr=expr, name="__getitem__"))
paulb@10 477
        elif flags == "OP_DELETE":
paulb@10 478
            args = []
paulb@10 479
            result = Invoke(expr=LoadAttr(expr=expr, name="__delitem__"))
paulb@10 480
        else:
paulb@10 481
            raise NotImplementedError, flags
paulb@10 482
paulb@10 483
        # Add the dimensions.
paulb@10 484
paulb@10 485
        args.insert(0, subs)
paulb@10 486
paulb@11 487
        result.original = subscript
paulb@10 488
        result.args = args
paulb@10 489
        return result
paulb@10 490
paulb@10 491
    def _visitSubscriptSubs(self, subs):
paulb@10 492
        if len(subs) == 1:
paulb@10 493
            return self.dispatch(subs[0])
paulb@10 494
        else:
paulb@10 495
            return Invoke(expr=LoadName(name="Tuple"), args=self.dispatches(subs))
paulb@10 496
paulb@10 497
    def visitSubscript(self, subscript, in_sequence=0):
paulb@10 498
        value = self._visitAssNameOrAttr(subscript, in_sequence)
paulb@10 499
        subs = self._visitSubscriptSubs(subscript.subs)
paulb@11 500
        return self._visitSubscript(subscript, self.dispatch(subscript.expr), subs, subscript.flags, value)
paulb@10 501
paul@0 502
    # Invocation and subprogram transformations.
paul@0 503
paul@0 504
    def _visitFunction(self, function, subprogram):
paul@0 505
        if function.flags & 4 != 0: has_star = 1
paul@0 506
        else: has_star = 0
paul@0 507
        if function.flags & 8 != 0: has_dstar = 1
paul@0 508
        else: has_dstar = 0
paul@0 509
        ndefaults = len(function.defaults)
paul@0 510
        npositional = len(function.argnames) - has_star - has_dstar
paul@0 511
        if has_star: star = function.argnames[npositional]
paul@0 512
        else: star = None
paul@0 513
        if has_dstar: dstar = function.argnames[npositional + has_star]
paul@0 514
        else: dstar = None
paul@0 515
paul@0 516
        params = []
paul@0 517
        for i in range(0, npositional - ndefaults):
paul@0 518
            params.append((function.argnames[i], None))
paul@0 519
paul@0 520
        # NOTE: Fix/process defaults.
paul@0 521
paul@0 522
        for i in range(0, ndefaults):
paul@0 523
            default = function.defaults[i]
paul@0 524
            if default is not None:
paul@0 525
                params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default)))
paul@0 526
            else:
paul@0 527
                params.append((function.argnames[npositional - ndefaults + i], default))
paul@0 528
paul@0 529
        subprogram.params = params
paul@0 530
        subprogram.star = star
paul@0 531
        subprogram.dstar = dstar
paul@0 532
        self.subprograms.append(subprogram)
paul@0 533
paul@0 534
    def visitFunction(self, function):
paul@0 535
paul@0 536
        # Make a subprogram for the function and record it outside the main
paul@0 537
        # tree.
paul@0 538
paulb@11 539
        subprogram = Subprogram(name=function.name, returns_value=1, star=None, dstar=None)
paul@0 540
        self.current_subprograms.append(subprogram)
paul@0 541
        subprogram.code = self.dispatch(function.code)
paul@0 542
        self.current_subprograms.pop()
paul@0 543
        self._visitFunction(function, subprogram)
paul@0 544
paul@0 545
        # Make a definition of the function associating it with a name.
paul@0 546
paul@0 547
        result = Assign(function)
paul@0 548
        load = LoadRef(ref=subprogram)
paul@0 549
        store = StoreName(name=function.name)
paul@0 550
        result.code = [load, store]
paul@0 551
        return result
paul@0 552
paul@0 553
    def visitLambda(self, lambda_):
paul@0 554
paul@0 555
        # Make a subprogram for the function and record it outside the main
paul@0 556
        # tree.
paul@0 557
paulb@11 558
        subprogram = Subprogram(name=hex(id(lambda_)), returns_value=1, star=None, dstar=None)
paul@0 559
        self.current_subprograms.append(subprogram)
paul@0 560
        subprogram.code = [Return(expr=self.dispatch(lambda_.code))]
paul@0 561
        self.current_subprograms.pop()
paul@0 562
        self._visitFunction(lambda_, subprogram)
paul@0 563
paul@0 564
        # Get the subprogram reference to the lambda.
paul@0 565
paulb@11 566
        return LoadRef(lambda_, ref=subprogram)
paul@0 567
paul@0 568
    def visitCallFunc(self, callfunc):
paul@0 569
        result = Invoke(callfunc, same_frame=0, star=None, dstar=None)
paul@0 570
        result.args = self.dispatches(callfunc.args)
paul@0 571
        if callfunc.star_args is not None:
paul@0 572
            result.star = self.dispatch(callfunc.star_args)
paul@0 573
        if callfunc.dstar_args is not None:
paul@0 574
            result.dstar = self.dispatch(callfunc.dstar_args)
paul@0 575
        result.expr = self.dispatch(callfunc.node)
paul@0 576
        return result
paul@0 577
paul@0 578
    def visitWhile(self, while_):
paul@0 579
paul@0 580
        # Make a subprogram for the block and record it outside the main tree.
paul@0 581
paulb@11 582
        subprogram = Subprogram(name=hex(id(while_)), acquire_locals=1, returns_value=0, params=[], star=None, dstar=None)
paul@0 583
        self.current_subprograms.append(subprogram)
paul@0 584
paul@0 585
        # Include a conditional statement in the subprogram.
paul@0 586
paul@0 587
        test = Conditional(else_=[])
paul@0 588
        test.test = Invoke(expr=LoadAttr(expr=self.dispatch(while_.test), name="__true__"),
paul@0 589
            params=[], star=None, dstar=None)
paul@0 590
paul@0 591
        # Inside the conditional, add a recursive invocation to the subprogram
paul@0 592
        # if the test condition was satisfied.
paul@0 593
paul@0 594
        continuation = Invoke(same_frame=1, star=None, dstar=None, args=[])
paul@0 595
        continuation.expr = LoadRef(ref=subprogram)
paul@0 596
        test.body = self.dispatch(while_.body) + [continuation]
paul@0 597
        if while_.else_ is not None:
paul@0 598
            test.else_ = self.dispatch(while_.else_)
paul@0 599
        subprogram.code = [test]
paul@0 600
paul@0 601
        self.current_subprograms.pop()
paul@0 602
        self.subprograms.append(subprogram)
paul@0 603
paul@0 604
        # Make an invocation of the subprogram.
paul@0 605
paulb@7 606
        result = Invoke(while_, same_frame=1, produces_result=0, star=None, dstar=None, args=[])
paul@0 607
        result.expr = LoadRef(ref=subprogram)
paul@0 608
        return result
paul@0 609
paul@0 610
    def visitFor(self, for_):
paul@0 611
paul@0 612
        # Make a subprogram for the block and record it outside the main tree.
paul@0 613
paulb@11 614
        subprogram = Subprogram(name=hex(id(for_)), acquire_locals=1, returns_value=0, params=[], star=None, dstar=None)
paul@0 615
        self.current_subprograms.append(subprogram)
paul@0 616
paul@0 617
        # Wrap the assignment in a try...except statement.
paul@0 618
paul@0 619
        try_except = Try(body=[], handlers=[], else_=[], finally_=[])
paul@0 620
        except_spec = Invoke(expr=LoadName(name="Tuple"), params=[LoadName(name="StopIteration")])
paul@0 621
        stopiteration = Except(spec=except_spec)
paul@0 622
        stopiteration.code = self.dispatch(for_.else_)
paul@0 623
        try_except.handlers = [stopiteration]
paul@0 624
paul@0 625
        assign = Assign()
paul@0 626
        assign.code = [
paul@0 627
            StoreTemp(expr=Invoke(expr=LoadAttr(expr=LoadTemp(), name="next"))),
paul@0 628
            self.dispatch(for_.assign),
paul@0 629
            ReleaseTemp()
paul@0 630
            ]
paul@0 631
paul@0 632
        # Inside the conditional, add a recursive invocation to the subprogram
paul@0 633
        # if the test condition was satisfied.
paul@0 634
paulb@7 635
        continuation = Invoke(same_frame=1, produces_result=0, star=None, dstar=None, args=[])
paul@0 636
        continuation.expr = LoadRef(ref=subprogram)
paul@0 637
        try_except.body = [assign] + self.dispatch(for_.body) + [continuation]
paul@0 638
        subprogram.code = [try_except]
paul@0 639
paul@0 640
        self.subprograms.append(subprogram)
paul@0 641
        self.current_subprograms.pop()
paul@0 642
paul@0 643
        # Obtain an iterator for the sequence involved.
paul@0 644
        # Then, make an invocation of the subprogram.
paul@0 645
paul@0 646
        result = Assign(for_)
paul@0 647
        result.code = [
paul@0 648
            StoreTemp(expr=Invoke(expr=LoadAttr(name="__iter__", expr=self.dispatch(for_.list)))),
paulb@7 649
            Invoke(expr=LoadRef(ref=subprogram), same_frame=1, produces_result=0, star=None, dstar=None, args=[]),
paul@0 650
            ReleaseTemp()
paul@0 651
            ]
paul@0 652
        return result
paul@0 653
paul@0 654
    # Exception node transformations.
paul@0 655
paul@0 656
    def visitTryExcept(self, tryexcept):
paul@0 657
        result = Try(tryexcept, body=[], handlers=[], else_=[], finally_=[])
paul@0 658
        if tryexcept.body is not None:
paul@0 659
            result.body = self.dispatch(tryexcept.body)
paul@0 660
        if tryexcept.else_ is not None:
paul@0 661
            result.else_ = self.dispatch(tryexcept.else_)
paul@0 662
        handlers = []
paul@0 663
        for spec, assign, stmt in tryexcept.handlers:
paul@0 664
            get_exc = Assign()
paul@5 665
            get_exc.code = [StoreTemp(expr=LoadExc())]
paul@5 666
            if assign is not None:
paul@5 667
                get_exc.code.append(self.dispatch(assign))
paul@5 668
            get_exc.code.append(ReleaseTemp())
paul@5 669
            handler = Except()
paul@5 670
            if spec is not None:
paul@5 671
                handler.spec = self.dispatch(spec)
paul@0 672
            handler.code = [get_exc] + self.dispatch(stmt)
paul@0 673
            handlers.append(handler)
paul@0 674
        result.handlers = handlers
paul@0 675
        return result
paul@0 676
paul@0 677
    def visitTryFinally(self, tryfinally):
paul@0 678
        result = Try(tryfinally, body=[], handlers=[], else_=[], finally_=[])
paul@0 679
        if tryfinally.body is not None:
paul@0 680
            result.body = self.dispatch(tryfinally.body)
paul@0 681
        if tryfinally.final is not None:
paul@0 682
            result.finally_ = self.dispatch(tryfinally.final)
paul@0 683
        return result
paul@0 684
paul@0 685
# vim: tabstop=4 expandtab shiftwidth=4