simplify

Annotated simplify.py

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