| 1 | import itertools
 | 
| 2 | 
 | 
| 3 | from . import ast, pyassem, misc
 | 
| 4 | from .visitor import ASTVisitor
 | 
| 5 | from .consts import (
 | 
| 6 |     SC_LOCAL, SC_GLOBAL_IMPLICIT, SC_GLOBAL_EXPLICIT, SC_FREE, SC_CELL)
 | 
| 7 | 
 | 
| 8 | # NOTE: removed CO_NESTED because it is unused
 | 
| 9 | from .consts import (
 | 
| 10 |     CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,
 | 
| 11 |     CO_GENERATOR, CO_FUTURE_DIVISION,
 | 
| 12 |     CO_FUTURE_ABSIMPORT, CO_FUTURE_WITH_STATEMENT, CO_FUTURE_PRINT_FUNCTION)
 | 
| 13 | 
 | 
| 14 | _CALLFUNC_OPCODE_INFO = {
 | 
| 15 |     # (Have *args, Have **args) : opcode
 | 
| 16 |     (0,0) : "CALL_FUNCTION",
 | 
| 17 |     (1,0) : "CALL_FUNCTION_VAR",
 | 
| 18 |     (0,1) : "CALL_FUNCTION_KW",
 | 
| 19 |     (1,1) : "CALL_FUNCTION_VAR_KW",
 | 
| 20 | }
 | 
| 21 | 
 | 
| 22 | _AUGMENTED_OPCODE = {
 | 
| 23 |     '+=' : 'INPLACE_ADD',
 | 
| 24 |     '-=' : 'INPLACE_SUBTRACT',
 | 
| 25 |     '*=' : 'INPLACE_MULTIPLY',
 | 
| 26 |     '/=' : 'INPLACE_DIVIDE',
 | 
| 27 |     '//=': 'INPLACE_FLOOR_DIVIDE',
 | 
| 28 |     '%=' : 'INPLACE_MODULO',
 | 
| 29 |     '**=': 'INPLACE_POWER',
 | 
| 30 |     '>>=': 'INPLACE_RSHIFT',
 | 
| 31 |     '<<=': 'INPLACE_LSHIFT',
 | 
| 32 |     '&=' : 'INPLACE_AND',
 | 
| 33 |     '^=' : 'INPLACE_XOR',
 | 
| 34 |     '|=' : 'INPLACE_OR',
 | 
| 35 | }
 | 
| 36 | 
 | 
| 37 | 
 | 
| 38 | LOOP = 1
 | 
| 39 | EXCEPT = 2
 | 
| 40 | TRY_FINALLY = 3
 | 
| 41 | END_FINALLY = 4
 | 
| 42 | 
 | 
| 43 | 
 | 
| 44 | # TODO: Move this to _ModuleContext so it's not a mutable global?
 | 
| 45 | gLambdaCounter = itertools.count()
 | 
| 46 | 
 | 
| 47 | 
 | 
| 48 | class LocalNameFinder(ASTVisitor):
 | 
| 49 |     """Find local names in scope"""
 | 
| 50 |     def __init__(self, names=None):
 | 
| 51 |         ASTVisitor.__init__(self)
 | 
| 52 |         self.names = names or set()
 | 
| 53 |         self.globals = set()
 | 
| 54 | 
 | 
| 55 |     # XXX list comprehensions and for loops
 | 
| 56 | 
 | 
| 57 |     def getLocals(self):
 | 
| 58 |         # TODO: set difference
 | 
| 59 |         for elt in self.globals:
 | 
| 60 |             if elt in self.names:
 | 
| 61 |                 self.names.remove(elt)
 | 
| 62 |         return self.names
 | 
| 63 | 
 | 
| 64 |     def visitDict(self, node):
 | 
| 65 |         pass
 | 
| 66 | 
 | 
| 67 |     def visitGlobal(self, node):
 | 
| 68 |         for name in node.names:
 | 
| 69 |             self.globals.add(name)
 | 
| 70 | 
 | 
| 71 |     def visitFunction(self, node):
 | 
| 72 |         self.names.add(node.name)
 | 
| 73 | 
 | 
| 74 |     def visitLambda(self, node):
 | 
| 75 |         pass
 | 
| 76 | 
 | 
| 77 |     def visitImport(self, node):
 | 
| 78 |         for name, alias in node.names:
 | 
| 79 |             self.names.add(alias or name)
 | 
| 80 | 
 | 
| 81 |     def visitFrom(self, node):
 | 
| 82 |         for name, alias in node.names:
 | 
| 83 |             self.names.add(alias or name)
 | 
| 84 | 
 | 
| 85 |     def visitClass(self, node):
 | 
| 86 |         self.names.add(node.name)
 | 
| 87 | 
 | 
| 88 |     def visitAssName(self, node):
 | 
| 89 |         self.names.add(node.name)
 | 
| 90 | 
 | 
| 91 | 
 | 
| 92 | def is_constant_false(node):
 | 
| 93 |     return isinstance(node, ast.Const) and not node.value
 | 
| 94 | 
 | 
| 95 | 
 | 
| 96 | class Stack(list):
 | 
| 97 | 
 | 
| 98 |     def push(self, elt):
 | 
| 99 |         self.append(elt)
 | 
| 100 | 
 | 
| 101 |     def top(self):
 | 
| 102 |         return self[-1]
 | 
| 103 | 
 | 
| 104 | 
 | 
| 105 | class CodeGenerator(ASTVisitor):
 | 
| 106 |     """Abstract class for modules, classes, functions."""
 | 
| 107 | 
 | 
| 108 |     def __init__(self, ctx, frame, graph):
 | 
| 109 |         ASTVisitor.__init__(self)
 | 
| 110 |         self.ctx = ctx  # passed down to child CodeGenerator instances
 | 
| 111 |         self.frame = frame
 | 
| 112 |         self.graph = graph
 | 
| 113 | 
 | 
| 114 |         # Set by visitModule, visitExpression (for eval), or by subclass
 | 
| 115 |         # constructor.
 | 
| 116 |         self.scope = None
 | 
| 117 |         self.class_name = None  # For name mangling; set by subclasses.
 | 
| 118 | 
 | 
| 119 |         self.locals = Stack()
 | 
| 120 |         self.setups = Stack()
 | 
| 121 |         self.__with_count = 0
 | 
| 122 |         self.last_lineno = None
 | 
| 123 |         self._div_op = "BINARY_DIVIDE"
 | 
| 124 | 
 | 
| 125 |         # Delegate methods to graph object
 | 
| 126 |         self.emit = self.graph.emit
 | 
| 127 |         self.newBlock = self.graph.newBlock
 | 
| 128 |         self.startBlock = self.graph.startBlock
 | 
| 129 |         self.nextBlock = self.graph.nextBlock
 | 
| 130 | 
 | 
| 131 |         # Set flags based on future features
 | 
| 132 |         for feature in ctx.futures:
 | 
| 133 |             if feature == "division":
 | 
| 134 |                 self.frame.setFlag(CO_FUTURE_DIVISION)
 | 
| 135 |                 self._div_op = "BINARY_TRUE_DIVIDE"
 | 
| 136 |             elif feature == "absolute_import":
 | 
| 137 |                 self.frame.setFlag(CO_FUTURE_ABSIMPORT)
 | 
| 138 |             elif feature == "with_statement":
 | 
| 139 |                 self.frame.setFlag(CO_FUTURE_WITH_STATEMENT)
 | 
| 140 |             elif feature == "print_function":
 | 
| 141 |                 self.frame.setFlag(CO_FUTURE_PRINT_FUNCTION)
 | 
| 142 | 
 | 
| 143 |     #
 | 
| 144 |     # Two methods that subclasses must implement.
 | 
| 145 |     #
 | 
| 146 | 
 | 
| 147 |     def _Start(self):
 | 
| 148 |         raise NotImplementedError
 | 
| 149 | 
 | 
| 150 |     def Finish(self):
 | 
| 151 |         raise NotImplementedError
 | 
| 152 | 
 | 
| 153 |     def Start(self):
 | 
| 154 |         self.frame.setVars(self.scope.get_free_vars(),
 | 
| 155 |                            self.scope.get_cell_vars())
 | 
| 156 |         self._Start()
 | 
| 157 | 
 | 
| 158 |     def _mangle(self, name):
 | 
| 159 |         return misc.mangle(name, self.class_name)
 | 
| 160 | 
 | 
| 161 |     def _optimized(self):
 | 
| 162 |         """Is namespace access optimized?"""
 | 
| 163 |         return False
 | 
| 164 | 
 | 
| 165 |     # Next five methods handle name access
 | 
| 166 | 
 | 
| 167 |     def storeName(self, name):
 | 
| 168 |         self._nameOp('STORE', name)
 | 
| 169 | 
 | 
| 170 |     def loadName(self, name):
 | 
| 171 |         self._nameOp('LOAD', name)
 | 
| 172 | 
 | 
| 173 |     def delName(self, name):
 | 
| 174 |         self._nameOp('DELETE', name)
 | 
| 175 | 
 | 
| 176 |     def _nameOp(self, prefix, name):
 | 
| 177 |         name = self._mangle(name)
 | 
| 178 |         scope = self.scope.check_name(name)
 | 
| 179 | 
 | 
| 180 |         if scope == SC_LOCAL:
 | 
| 181 |             suffix = 'FAST' if self._optimized() else 'NAME'
 | 
| 182 |             self.emit('%s_%s' % (prefix, suffix), name)
 | 
| 183 | 
 | 
| 184 |         elif scope == SC_GLOBAL_EXPLICIT:
 | 
| 185 |             self.emit(prefix + '_GLOBAL', name)
 | 
| 186 | 
 | 
| 187 |         elif scope == SC_GLOBAL_IMPLICIT:
 | 
| 188 |             suffix = 'GLOBAL' if self._optimized() else 'NAME'
 | 
| 189 |             self.emit('%s_%s' % (prefix, suffix), name)
 | 
| 190 | 
 | 
| 191 |         elif scope == SC_FREE or scope == SC_CELL:
 | 
| 192 |             self.emit(prefix + '_DEREF', name)
 | 
| 193 | 
 | 
| 194 |         else:
 | 
| 195 |             raise RuntimeError, "unsupported scope for var %s: %d" % \
 | 
| 196 |                   (name, scope)
 | 
| 197 | 
 | 
| 198 |     def _implicitNameOp(self, prefix, name):
 | 
| 199 |         """Emit name ops for names generated implicitly by for loops
 | 
| 200 | 
 | 
| 201 |         The interpreter generates names that start with a period or
 | 
| 202 |         dollar sign.  The symbol table ignores these names because
 | 
| 203 |         they aren't present in the program text.
 | 
| 204 |         """
 | 
| 205 |         suffix = 'FAST' if self._optimized() else 'NAME'
 | 
| 206 |         self.emit('%s_%s' % (prefix, suffix), name)
 | 
| 207 | 
 | 
| 208 |     # The set_lineno() function and the explicit emit() calls for
 | 
| 209 |     # SET_LINENO below are only used to generate the line number table.
 | 
| 210 |     # As of Python 2.3, the interpreter does not have a SET_LINENO
 | 
| 211 |     # instruction.  pyassem treats SET_LINENO opcodes as a special case.
 | 
| 212 | 
 | 
| 213 |     def set_lineno(self, node, force=False):
 | 
| 214 |         """Emit SET_LINENO if necessary.
 | 
| 215 | 
 | 
| 216 |         The instruction is considered necessary if the node has a
 | 
| 217 |         lineno attribute and it is different than the last lineno
 | 
| 218 |         emitted.
 | 
| 219 | 
 | 
| 220 |         Returns true if SET_LINENO was emitted.
 | 
| 221 | 
 | 
| 222 |         There are no rules for when an AST node should have a lineno
 | 
| 223 |         attribute.  The transformer and AST code need to be reviewed
 | 
| 224 |         and a consistent policy implemented and documented.  Until
 | 
| 225 |         then, this method works around missing line numbers.
 | 
| 226 |         """
 | 
| 227 |         lineno = getattr(node, 'lineno', None)
 | 
| 228 |         if lineno is not None and (lineno != self.last_lineno or force):
 | 
| 229 |             self.emit('SET_LINENO', lineno)
 | 
| 230 |             self.last_lineno = lineno
 | 
| 231 |             return True
 | 
| 232 |         return False
 | 
| 233 | 
 | 
| 234 |     def visitModule(self, node):
 | 
| 235 |         self.scope = self.ctx.scopes[node]
 | 
| 236 |         self.emit('SET_LINENO', 0)
 | 
| 237 |         if node.doc and self.ctx.comp_opt.emit_docstring:
 | 
| 238 |             self.emit('LOAD_CONST', node.doc)
 | 
| 239 |             self.storeName('__doc__')
 | 
| 240 | 
 | 
| 241 |         lnf = LocalNameFinder()
 | 
| 242 |         lnf.Dispatch(node.node)
 | 
| 243 | 
 | 
| 244 |         self.locals.push(lnf.getLocals())
 | 
| 245 |         self.visit(node.node)
 | 
| 246 |         self.emit('LOAD_CONST', None)
 | 
| 247 |         self.emit('RETURN_VALUE')
 | 
| 248 | 
 | 
| 249 |     def visitExpression(self, node):
 | 
| 250 |         """Expression is an artificial node to support "eval".
 | 
| 251 | 
 | 
| 252 |         TODO: Could be renamed EvalModule?
 | 
| 253 |         """
 | 
| 254 |         self.set_lineno(node)
 | 
| 255 |         self.scope = self.ctx.scopes[node]
 | 
| 256 |         self.visit(node.node)
 | 
| 257 |         self.emit('RETURN_VALUE')
 | 
| 258 | 
 | 
| 259 |     # Differences between functions and lambdas:
 | 
| 260 |     # - lambdas need an auto-generated name for the code object
 | 
| 261 |     # - lamdbda don't have docstrings
 | 
| 262 |     # - lambdas can't have decorators
 | 
| 263 |     # - code gen: non-lambdas need an extra LOAD_CONST at the end, see Finish()
 | 
| 264 | 
 | 
| 265 |     def visitFunction(self, node):
 | 
| 266 |         if node.decorators:
 | 
| 267 |             for decorator in node.decorators.nodes:
 | 
| 268 |                 self.visit(decorator)
 | 
| 269 |             ndecorators = len(node.decorators.nodes)
 | 
| 270 |         else:
 | 
| 271 |             ndecorators = 0
 | 
| 272 | 
 | 
| 273 |         _CheckNoTupleArgs(node)
 | 
| 274 | 
 | 
| 275 |         frame = pyassem.Frame(node.name, self.ctx.filename, optimized=1)
 | 
| 276 |         frame.setArgs(node.argnames)
 | 
| 277 |         graph = pyassem.FlowGraph()
 | 
| 278 | 
 | 
| 279 |         # NOTE: This is a new CodeGenerator instance because each function has
 | 
| 280 |         # its own scope.
 | 
| 281 |         gen = FunctionCodeGenerator(self.ctx, frame, graph, node,
 | 
| 282 |                                     self.class_name)
 | 
| 283 | 
 | 
| 284 |         self._funcOrLambda(node, gen, ndecorators)
 | 
| 285 | 
 | 
| 286 |         # TODO: This seems like a bug.  We already setDocstring in FindLocals()
 | 
| 287 |         # on the FunctionCodeGenerator below.  This seems to mean that the
 | 
| 288 |         # MODULE gets the docstring of the last function?  But this changes the
 | 
| 289 |         # output.
 | 
| 290 |         if node.doc:
 | 
| 291 |             self.frame.setDocstring(node.doc)
 | 
| 292 |         self.storeName(node.name)
 | 
| 293 | 
 | 
| 294 |     def visitLambda(self, node):
 | 
| 295 |         obj_name = "<lambda.%d>" % gLambdaCounter.next()
 | 
| 296 | 
 | 
| 297 |         _CheckNoTupleArgs(node)
 | 
| 298 |         frame = pyassem.Frame(obj_name, self.ctx.filename, optimized=1)
 | 
| 299 |         frame.setArgs(node.argnames)
 | 
| 300 |         graph = pyassem.FlowGraph()
 | 
| 301 | 
 | 
| 302 |         gen = LambdaCodeGenerator(self.ctx, frame, graph, node,
 | 
| 303 |                                   self.class_name)
 | 
| 304 | 
 | 
| 305 |         self._funcOrLambda(node, gen, 0)
 | 
| 306 | 
 | 
| 307 |     def _funcOrLambda(self, node, gen, ndecorators):
 | 
| 308 |         """Helper for visitFunction and visitLambda."""
 | 
| 309 |         gen.Start()
 | 
| 310 |         gen.FindLocals()
 | 
| 311 |         gen.Dispatch(node.code)
 | 
| 312 |         gen.Finish()
 | 
| 313 | 
 | 
| 314 |         self.set_lineno(node)
 | 
| 315 |         for default in node.defaults:
 | 
| 316 |             self.visit(default)
 | 
| 317 |         self._makeClosure(gen, len(node.defaults))
 | 
| 318 |         for i in xrange(ndecorators):
 | 
| 319 |             self.emit('CALL_FUNCTION', 1)
 | 
| 320 | 
 | 
| 321 |     def visitClass(self, node):
 | 
| 322 |         frame = pyassem.Frame(node.name, self.ctx.filename, optimized=0, klass=1)
 | 
| 323 |         graph = pyassem.FlowGraph()
 | 
| 324 |         gen = ClassCodeGenerator(self.ctx, frame, graph, node)
 | 
| 325 | 
 | 
| 326 |         gen.Start()
 | 
| 327 |         gen.FindLocals()
 | 
| 328 |         gen.Dispatch(node.code)
 | 
| 329 |         gen.Finish()
 | 
| 330 | 
 | 
| 331 |         self.set_lineno(node)
 | 
| 332 |         self.emit('LOAD_CONST', node.name)
 | 
| 333 |         for base in node.bases:
 | 
| 334 |             self.visit(base)
 | 
| 335 |         self.emit('BUILD_TUPLE', len(node.bases))
 | 
| 336 |         self._makeClosure(gen, 0)
 | 
| 337 |         self.emit('CALL_FUNCTION', 0)
 | 
| 338 |         self.emit('BUILD_CLASS')
 | 
| 339 |         self.storeName(node.name)
 | 
| 340 | 
 | 
| 341 |     # The next few implement control-flow statements
 | 
| 342 | 
 | 
| 343 |     def visitIf(self, node):
 | 
| 344 |         end = self.newBlock()
 | 
| 345 |         for i, (test, suite) in enumerate(node.tests):
 | 
| 346 |             if is_constant_false(test):
 | 
| 347 |                 # XXX will need to check generator stuff here
 | 
| 348 |                 continue
 | 
| 349 |             self.set_lineno(test)
 | 
| 350 |             self.visit(test)
 | 
| 351 |             nextTest = self.newBlock()
 | 
| 352 |             self.emit('POP_JUMP_IF_FALSE', nextTest)
 | 
| 353 |             self.nextBlock()
 | 
| 354 |             self.visit(suite)
 | 
| 355 |             self.emit('JUMP_FORWARD', end)
 | 
| 356 |             self.startBlock(nextTest)
 | 
| 357 |         if node.else_:
 | 
| 358 |             self.visit(node.else_)
 | 
| 359 |         self.nextBlock(end)
 | 
| 360 | 
 | 
| 361 |     def visitWhile(self, node):
 | 
| 362 |         self.set_lineno(node)
 | 
| 363 | 
 | 
| 364 |         loop = self.newBlock()
 | 
| 365 |         else_ = self.newBlock()
 | 
| 366 | 
 | 
| 367 |         after = self.newBlock()
 | 
| 368 |         self.emit('SETUP_LOOP', after)
 | 
| 369 | 
 | 
| 370 |         self.nextBlock(loop)
 | 
| 371 |         self.setups.push((LOOP, loop))
 | 
| 372 | 
 | 
| 373 |         self.set_lineno(node, force=True)
 | 
| 374 |         self.visit(node.test)
 | 
| 375 |         self.emit('POP_JUMP_IF_FALSE', else_ or after)
 | 
| 376 | 
 | 
| 377 |         self.nextBlock()
 | 
| 378 |         self.visit(node.body)
 | 
| 379 |         self.emit('JUMP_ABSOLUTE', loop)
 | 
| 380 | 
 | 
| 381 |         self.startBlock(else_) # or just the POPs if not else clause
 | 
| 382 |         self.emit('POP_BLOCK')
 | 
| 383 |         self.setups.pop()
 | 
| 384 |         if node.else_:
 | 
| 385 |             self.visit(node.else_)
 | 
| 386 |         self.nextBlock(after)
 | 
| 387 | 
 | 
| 388 |     def visitFor(self, node):
 | 
| 389 |         start = self.newBlock()
 | 
| 390 |         anchor = self.newBlock()
 | 
| 391 |         after = self.newBlock()
 | 
| 392 |         self.setups.push((LOOP, start))
 | 
| 393 | 
 | 
| 394 |         self.set_lineno(node)
 | 
| 395 |         self.emit('SETUP_LOOP', after)
 | 
| 396 |         self.visit(node.list)
 | 
| 397 |         self.emit('GET_ITER')
 | 
| 398 | 
 | 
| 399 |         self.nextBlock(start)
 | 
| 400 |         self.set_lineno(node, force=True)
 | 
| 401 |         self.emit('FOR_ITER', anchor)
 | 
| 402 |         self.visit(node.assign)
 | 
| 403 |         self.visit(node.body)
 | 
| 404 |         self.emit('JUMP_ABSOLUTE', start)
 | 
| 405 |         self.nextBlock(anchor)
 | 
| 406 |         self.emit('POP_BLOCK')
 | 
| 407 |         self.setups.pop()
 | 
| 408 |         if node.else_:
 | 
| 409 |             self.visit(node.else_)
 | 
| 410 |         self.nextBlock(after)
 | 
| 411 | 
 | 
| 412 |     def visitBreak(self, node):
 | 
| 413 |         if not self.setups:
 | 
| 414 |             raise SyntaxError, "'break' outside loop (%s, %d)" % \
 | 
| 415 |                   (self.ctx.filename, node.lineno)
 | 
| 416 |         self.set_lineno(node)
 | 
| 417 |         self.emit('BREAK_LOOP')
 | 
| 418 | 
 | 
| 419 |     def visitContinue(self, node):
 | 
| 420 |         if not self.setups:
 | 
| 421 |             raise SyntaxError, "'continue' outside loop (%s, %d)" % \
 | 
| 422 |                   (self.ctx.filename, node.lineno)
 | 
| 423 |         kind, block = self.setups.top()
 | 
| 424 |         if kind == LOOP:
 | 
| 425 |             self.set_lineno(node)
 | 
| 426 |             self.emit('JUMP_ABSOLUTE', block)
 | 
| 427 |             self.nextBlock()
 | 
| 428 |         elif kind == EXCEPT or kind == TRY_FINALLY:
 | 
| 429 |             self.set_lineno(node)
 | 
| 430 |             # find the block that starts the loop
 | 
| 431 |             top = len(self.setups)
 | 
| 432 |             while top > 0:
 | 
| 433 |                 top = top - 1
 | 
| 434 |                 kind, loop_block = self.setups[top]
 | 
| 435 |                 if kind == LOOP:
 | 
| 436 |                     break
 | 
| 437 |             if kind != LOOP:
 | 
| 438 |                 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
 | 
| 439 |                       (self.ctx.filename, node.lineno)
 | 
| 440 |             self.emit('CONTINUE_LOOP', loop_block)
 | 
| 441 |             self.nextBlock()
 | 
| 442 |         elif kind == END_FINALLY:
 | 
| 443 |             msg = "'continue' not allowed inside 'finally' clause (%s, %d)"
 | 
| 444 |             raise SyntaxError, msg % (self.ctx.filename, node.lineno)
 | 
| 445 | 
 | 
| 446 |     def visitTest(self, node, jump):
 | 
| 447 |         end = self.newBlock()
 | 
| 448 |         for child in node.nodes[:-1]:
 | 
| 449 |             self.visit(child)
 | 
| 450 |             self.emit(jump, end)
 | 
| 451 |             self.nextBlock()
 | 
| 452 |         self.visit(node.nodes[-1])
 | 
| 453 |         self.nextBlock(end)
 | 
| 454 | 
 | 
| 455 |     def visitAnd(self, node):
 | 
| 456 |         self.visitTest(node, 'JUMP_IF_FALSE_OR_POP')
 | 
| 457 | 
 | 
| 458 |     def visitOr(self, node):
 | 
| 459 |         self.visitTest(node, 'JUMP_IF_TRUE_OR_POP')
 | 
| 460 | 
 | 
| 461 |     def visitIfExp(self, node):
 | 
| 462 |         endblock = self.newBlock()
 | 
| 463 |         elseblock = self.newBlock()
 | 
| 464 |         self.visit(node.test)
 | 
| 465 |         self.emit('POP_JUMP_IF_FALSE', elseblock)
 | 
| 466 |         self.visit(node.then)
 | 
| 467 |         self.emit('JUMP_FORWARD', endblock)
 | 
| 468 |         self.nextBlock(elseblock)
 | 
| 469 |         self.visit(node.else_)
 | 
| 470 |         self.nextBlock(endblock)
 | 
| 471 | 
 | 
| 472 |     def visitCompare(self, node):
 | 
| 473 |         self.visit(node.expr)
 | 
| 474 |         cleanup = self.newBlock()
 | 
| 475 |         for op, code in node.ops[:-1]:
 | 
| 476 |             self.visit(code)
 | 
| 477 |             self.emit('DUP_TOP')
 | 
| 478 |             self.emit('ROT_THREE')
 | 
| 479 |             self.emit('COMPARE_OP', op)
 | 
| 480 |             self.emit('JUMP_IF_FALSE_OR_POP', cleanup)
 | 
| 481 |             self.nextBlock()
 | 
| 482 |         # now do the last comparison
 | 
| 483 |         if node.ops:
 | 
| 484 |             op, code = node.ops[-1]
 | 
| 485 |             self.visit(code)
 | 
| 486 |             self.emit('COMPARE_OP', op)
 | 
| 487 |         if len(node.ops) > 1:
 | 
| 488 |             end = self.newBlock()
 | 
| 489 |             self.emit('JUMP_FORWARD', end)
 | 
| 490 |             self.startBlock(cleanup)
 | 
| 491 |             self.emit('ROT_TWO')
 | 
| 492 |             self.emit('POP_TOP')
 | 
| 493 |             self.nextBlock(end)
 | 
| 494 | 
 | 
| 495 |     # list comprehensions
 | 
| 496 |     def visitListComp(self, node):
 | 
| 497 |         self.set_lineno(node)
 | 
| 498 |         # setup list
 | 
| 499 |         self.emit('BUILD_LIST', 0)
 | 
| 500 | 
 | 
| 501 |         stack = []
 | 
| 502 |         for i, for_ in enumerate(node.quals):
 | 
| 503 |             start, anchor = self.visit(for_)
 | 
| 504 |             cont = None
 | 
| 505 |             for if_ in for_.ifs:
 | 
| 506 |                 if cont is None:
 | 
| 507 |                     cont = self.newBlock()
 | 
| 508 |                 self.visit(if_, cont)
 | 
| 509 |             stack.insert(0, (start, cont, anchor))
 | 
| 510 | 
 | 
| 511 |         self.visit(node.expr)
 | 
| 512 |         self.emit('LIST_APPEND', len(node.quals) + 1)
 | 
| 513 | 
 | 
| 514 |         for start, cont, anchor in stack:
 | 
| 515 |             if cont:
 | 
| 516 |                 self.nextBlock(cont)
 | 
| 517 |             self.emit('JUMP_ABSOLUTE', start)
 | 
| 518 |             self.startBlock(anchor)
 | 
| 519 | 
 | 
| 520 |     def visitSetComp(self, node):
 | 
| 521 |         self.set_lineno(node)
 | 
| 522 |         # setup list
 | 
| 523 |         self.emit('BUILD_SET', 0)
 | 
| 524 | 
 | 
| 525 |         stack = []
 | 
| 526 |         for i, for_ in enumerate(node.quals):
 | 
| 527 |             start, anchor = self.visit(for_)
 | 
| 528 |             cont = None
 | 
| 529 |             for if_ in for_.ifs:
 | 
| 530 |                 if cont is None:
 | 
| 531 |                     cont = self.newBlock()
 | 
| 532 |                 self.visit(if_, cont)
 | 
| 533 |             stack.insert(0, (start, cont, anchor))
 | 
| 534 | 
 | 
| 535 |         self.visit(node.expr)
 | 
| 536 |         self.emit('SET_ADD', len(node.quals) + 1)
 | 
| 537 | 
 | 
| 538 |         for start, cont, anchor in stack:
 | 
| 539 |             if cont:
 | 
| 540 |                 self.nextBlock(cont)
 | 
| 541 |             self.emit('JUMP_ABSOLUTE', start)
 | 
| 542 |             self.startBlock(anchor)
 | 
| 543 | 
 | 
| 544 |     def visitDictComp(self, node):
 | 
| 545 |         self.set_lineno(node)
 | 
| 546 |         # setup list
 | 
| 547 |         self.emit('BUILD_MAP', 0)
 | 
| 548 | 
 | 
| 549 |         stack = []
 | 
| 550 |         for i, for_ in enumerate(node.quals):
 | 
| 551 |             start, anchor = self.visit(for_)
 | 
| 552 |             cont = None
 | 
| 553 |             for if_ in for_.ifs:
 | 
| 554 |                 if cont is None:
 | 
| 555 |                     cont = self.newBlock()
 | 
| 556 |                 self.visit(if_, cont)
 | 
| 557 |             stack.insert(0, (start, cont, anchor))
 | 
| 558 | 
 | 
| 559 |         self.visit(node.value)
 | 
| 560 |         self.visit(node.key)
 | 
| 561 |         self.emit('MAP_ADD', len(node.quals) + 1)
 | 
| 562 | 
 | 
| 563 |         for start, cont, anchor in stack:
 | 
| 564 |             if cont:
 | 
| 565 |                 self.nextBlock(cont)
 | 
| 566 |             self.emit('JUMP_ABSOLUTE', start)
 | 
| 567 |             self.startBlock(anchor)
 | 
| 568 | 
 | 
| 569 |     def visitListCompFor(self, node):
 | 
| 570 |         start = self.newBlock()
 | 
| 571 |         anchor = self.newBlock()
 | 
| 572 | 
 | 
| 573 |         self.visit(node.list)
 | 
| 574 |         self.emit('GET_ITER')
 | 
| 575 |         self.nextBlock(start)
 | 
| 576 |         self.set_lineno(node, force=True)
 | 
| 577 |         self.emit('FOR_ITER', anchor)
 | 
| 578 |         self.nextBlock()
 | 
| 579 |         self.visit(node.assign)
 | 
| 580 |         return start, anchor
 | 
| 581 | 
 | 
| 582 |     def visitListCompIf(self, node, branch):
 | 
| 583 |         self.set_lineno(node, force=True)
 | 
| 584 |         self.visit(node.test)
 | 
| 585 |         self.emit('POP_JUMP_IF_FALSE', branch)
 | 
| 586 |         self.newBlock()
 | 
| 587 | 
 | 
| 588 |     def _makeClosure(self, gen, args):
 | 
| 589 |         """Emit LOAD_CONST of this generator.
 | 
| 590 | 
 | 
| 591 |         ArgEncoder calls MakeCodeObject on it.  There are a few instructions
 | 
| 592 |         afterward, so I guess we can't do that here?
 | 
| 593 |         """
 | 
| 594 |         # NOTE: This scope analysis must be wrong, because we are outputting
 | 
| 595 |         # LOAD_CLOSURE.
 | 
| 596 |         frees = gen.scope.get_free_vars()
 | 
| 597 | 
 | 
| 598 |         # Recursive call!
 | 
| 599 |         co = pyassem.MakeCodeObject(gen.frame, gen.graph, self.ctx.comp_opt)
 | 
| 600 | 
 | 
| 601 |         if frees:
 | 
| 602 |             for name in frees:
 | 
| 603 |                 self.emit('LOAD_CLOSURE', name)
 | 
| 604 |             self.emit('BUILD_TUPLE', len(frees))
 | 
| 605 |             self.emit('LOAD_CONST', co)
 | 
| 606 |             self.emit('MAKE_CLOSURE', args)
 | 
| 607 |         else:
 | 
| 608 |             self.emit('LOAD_CONST', co)
 | 
| 609 |             self.emit('MAKE_FUNCTION', args)
 | 
| 610 | 
 | 
| 611 |     def visitGenExpr(self, node):
 | 
| 612 |         isLambda = 1  # TODO: Shouldn't be a lambda?  Note Finish().
 | 
| 613 |         if isLambda:
 | 
| 614 |             obj_name = "<lambda.%d>" % gLambdaCounter.next()
 | 
| 615 |         else:
 | 
| 616 |             # TODO: enable this.  This is more like CPython.  Note that I worked
 | 
| 617 |             # worked around a bug in byterun due to NOT having this.
 | 
| 618 |             # http://bugs.python.org/issue19611
 | 
| 619 |             # That workaround may no longer be necessary if we switch.
 | 
| 620 |             obj_name = '<genexpr>'
 | 
| 621 | 
 | 
| 622 |         frame = pyassem.Frame(obj_name, self.ctx.filename, optimized=1)
 | 
| 623 |         frame.setArgs(node.argnames)
 | 
| 624 |         graph = pyassem.FlowGraph()
 | 
| 625 |         gen = GenExprCodeGenerator(self.ctx, frame, graph, node,
 | 
| 626 |                                    self.class_name)
 | 
| 627 | 
 | 
| 628 |         gen.Start()
 | 
| 629 |         gen.FindLocals()
 | 
| 630 |         gen.Dispatch(node.code)
 | 
| 631 |         gen.Finish()
 | 
| 632 | 
 | 
| 633 |         self.set_lineno(node)
 | 
| 634 |         self._makeClosure(gen, 0)
 | 
| 635 |         # precomputation of outmost iterable
 | 
| 636 |         self.visit(node.code.quals[0].iter)
 | 
| 637 |         self.emit('GET_ITER')
 | 
| 638 |         self.emit('CALL_FUNCTION', 1)
 | 
| 639 | 
 | 
| 640 |     def visitGenExprInner(self, node):
 | 
| 641 |         self.set_lineno(node)
 | 
| 642 |         # setup list
 | 
| 643 | 
 | 
| 644 |         stack = []
 | 
| 645 |         for i, for_ in enumerate(node.quals):
 | 
| 646 |             start, anchor, end = self.visit(for_)
 | 
| 647 |             cont = None
 | 
| 648 |             for if_ in for_.ifs:
 | 
| 649 |                 if cont is None:
 | 
| 650 |                     cont = self.newBlock()
 | 
| 651 |                 self.visit(if_, cont)
 | 
| 652 |             stack.insert(0, (start, cont, anchor, end))
 | 
| 653 | 
 | 
| 654 |         self.visit(node.expr)
 | 
| 655 |         self.emit('YIELD_VALUE')
 | 
| 656 |         self.emit('POP_TOP')
 | 
| 657 | 
 | 
| 658 |         for start, cont, anchor, end in stack:
 | 
| 659 |             if cont:
 | 
| 660 |                 self.nextBlock(cont)
 | 
| 661 |             self.emit('JUMP_ABSOLUTE', start)
 | 
| 662 |             self.startBlock(anchor)
 | 
| 663 |             self.emit('POP_BLOCK')
 | 
| 664 |             self.setups.pop()
 | 
| 665 |             self.nextBlock(end)
 | 
| 666 | 
 | 
| 667 |         self.emit('LOAD_CONST', None)
 | 
| 668 | 
 | 
| 669 |     def visitGenExprFor(self, node):
 | 
| 670 |         start = self.newBlock()
 | 
| 671 |         anchor = self.newBlock()
 | 
| 672 |         end = self.newBlock()
 | 
| 673 | 
 | 
| 674 |         self.setups.push((LOOP, start))
 | 
| 675 |         self.emit('SETUP_LOOP', end)
 | 
| 676 | 
 | 
| 677 |         if node.is_outmost:
 | 
| 678 |             self.loadName('.0')
 | 
| 679 |         else:
 | 
| 680 |             self.visit(node.iter)
 | 
| 681 |             self.emit('GET_ITER')
 | 
| 682 | 
 | 
| 683 |         self.nextBlock(start)
 | 
| 684 |         self.set_lineno(node, force=True)
 | 
| 685 |         self.emit('FOR_ITER', anchor)
 | 
| 686 |         self.nextBlock()
 | 
| 687 |         self.visit(node.assign)
 | 
| 688 |         return start, anchor, end
 | 
| 689 | 
 | 
| 690 |     def visitGenExprIf(self, node, branch):
 | 
| 691 |         self.set_lineno(node, force=True)
 | 
| 692 |         self.visit(node.test)
 | 
| 693 |         self.emit('POP_JUMP_IF_FALSE', branch)
 | 
| 694 |         self.newBlock()
 | 
| 695 | 
 | 
| 696 |     # exception related
 | 
| 697 | 
 | 
| 698 |     def visitAssert(self, node):
 | 
| 699 |         # XXX would be interesting to implement this via a
 | 
| 700 |         # transformation of the AST before this stage
 | 
| 701 |         if __debug__:
 | 
| 702 |             end = self.newBlock()
 | 
| 703 |             self.set_lineno(node)
 | 
| 704 |             # XXX AssertionError appears to be special case -- it is always
 | 
| 705 |             # loaded as a global even if there is a local name.  I guess this
 | 
| 706 |             # is a sort of renaming op.
 | 
| 707 |             self.nextBlock()
 | 
| 708 |             self.visit(node.test)
 | 
| 709 |             self.emit('POP_JUMP_IF_TRUE', end)
 | 
| 710 |             self.nextBlock()
 | 
| 711 |             self.emit('LOAD_GLOBAL', 'AssertionError')
 | 
| 712 |             if node.fail:
 | 
| 713 |                 self.visit(node.fail)
 | 
| 714 |                 self.emit('RAISE_VARARGS', 2)
 | 
| 715 |             else:
 | 
| 716 |                 self.emit('RAISE_VARARGS', 1)
 | 
| 717 |             self.nextBlock(end)
 | 
| 718 | 
 | 
| 719 |     def visitRaise(self, node):
 | 
| 720 |         self.set_lineno(node)
 | 
| 721 |         n = 0
 | 
| 722 |         if node.expr1:
 | 
| 723 |             self.visit(node.expr1)
 | 
| 724 |             n = n + 1
 | 
| 725 |         if node.expr2:
 | 
| 726 |             self.visit(node.expr2)
 | 
| 727 |             n = n + 1
 | 
| 728 |         if node.expr3:
 | 
| 729 |             self.visit(node.expr3)
 | 
| 730 |             n = n + 1
 | 
| 731 |         self.emit('RAISE_VARARGS', n)
 | 
| 732 | 
 | 
| 733 |     def visitTryExcept(self, node):
 | 
| 734 |         body = self.newBlock()
 | 
| 735 |         handlers = self.newBlock()
 | 
| 736 |         end = self.newBlock()
 | 
| 737 |         if node.else_:
 | 
| 738 |             lElse = self.newBlock()
 | 
| 739 |         else:
 | 
| 740 |             lElse = end
 | 
| 741 |         self.set_lineno(node)
 | 
| 742 |         self.emit('SETUP_EXCEPT', handlers)
 | 
| 743 |         self.nextBlock(body)
 | 
| 744 |         self.setups.push((EXCEPT, body))
 | 
| 745 |         self.visit(node.body)
 | 
| 746 |         self.emit('POP_BLOCK')
 | 
| 747 |         self.setups.pop()
 | 
| 748 |         self.emit('JUMP_FORWARD', lElse)
 | 
| 749 |         self.startBlock(handlers)
 | 
| 750 | 
 | 
| 751 |         last = len(node.handlers) - 1
 | 
| 752 |         for i, (expr, target, body) in enumerate(node.handlers):
 | 
| 753 |             expr, target, body = node.handlers[i]
 | 
| 754 |             self.set_lineno(expr)
 | 
| 755 |             if expr:
 | 
| 756 |                 self.emit('DUP_TOP')
 | 
| 757 |                 self.visit(expr)
 | 
| 758 |                 self.emit('COMPARE_OP', 'exception match')
 | 
| 759 |                 next = self.newBlock()
 | 
| 760 |                 self.emit('POP_JUMP_IF_FALSE', next)
 | 
| 761 |                 self.nextBlock()
 | 
| 762 |             self.emit('POP_TOP')
 | 
| 763 |             if target:
 | 
| 764 |                 self.visit(target)
 | 
| 765 |             else:
 | 
| 766 |                 self.emit('POP_TOP')
 | 
| 767 |             self.emit('POP_TOP')
 | 
| 768 |             self.visit(body)
 | 
| 769 |             self.emit('JUMP_FORWARD', end)
 | 
| 770 |             if expr:
 | 
| 771 |                 self.nextBlock(next)
 | 
| 772 |             else:
 | 
| 773 |                 self.nextBlock()
 | 
| 774 |         self.emit('END_FINALLY')
 | 
| 775 |         if node.else_:
 | 
| 776 |             self.nextBlock(lElse)
 | 
| 777 |             self.visit(node.else_)
 | 
| 778 |         self.nextBlock(end)
 | 
| 779 | 
 | 
| 780 |     def visitTryFinally(self, node):
 | 
| 781 |         body = self.newBlock()
 | 
| 782 |         final = self.newBlock()
 | 
| 783 |         self.set_lineno(node)
 | 
| 784 |         self.emit('SETUP_FINALLY', final)
 | 
| 785 |         self.nextBlock(body)
 | 
| 786 |         self.setups.push((TRY_FINALLY, body))
 | 
| 787 |         self.visit(node.body)
 | 
| 788 |         self.emit('POP_BLOCK')
 | 
| 789 |         self.setups.pop()
 | 
| 790 |         self.emit('LOAD_CONST', None)
 | 
| 791 |         self.nextBlock(final)
 | 
| 792 |         self.setups.push((END_FINALLY, final))
 | 
| 793 |         self.visit(node.final)
 | 
| 794 |         self.emit('END_FINALLY')
 | 
| 795 |         self.setups.pop()
 | 
| 796 | 
 | 
| 797 |     def visitWith(self, node):
 | 
| 798 |         body = self.newBlock()
 | 
| 799 |         final = self.newBlock()
 | 
| 800 |         self.__with_count += 1
 | 
| 801 |         valuevar = "_[%d]" % self.__with_count
 | 
| 802 |         self.set_lineno(node)
 | 
| 803 |         self.visit(node.expr)
 | 
| 804 |         self.emit('DUP_TOP')
 | 
| 805 |         self.emit('LOAD_ATTR', '__exit__')
 | 
| 806 |         self.emit('ROT_TWO')
 | 
| 807 |         self.emit('LOAD_ATTR', '__enter__')
 | 
| 808 |         self.emit('CALL_FUNCTION', 0)
 | 
| 809 |         if node.vars is None:
 | 
| 810 |             self.emit('POP_TOP')
 | 
| 811 |         else:
 | 
| 812 |             self._implicitNameOp('STORE', valuevar)
 | 
| 813 |         self.emit('SETUP_FINALLY', final)
 | 
| 814 |         self.nextBlock(body)
 | 
| 815 |         self.setups.push((TRY_FINALLY, body))
 | 
| 816 | 
 | 
| 817 |         # TODO: This is buggy:
 | 
| 818 |         # - It generates LOAD_FAST and DELETE_FAST in bin/oil.py.
 | 
| 819 |         # The self.visit does nothing -- there are never any statements in between.
 | 
| 820 |         # - I think WITH_CLEANUP pops a block so this is # OK.
 | 
| 821 |         # - It never emits SETUP_WITH!  Doh!
 | 
| 822 | 
 | 
| 823 |         if node.vars is not None:
 | 
| 824 |             self._implicitNameOp('LOAD', valuevar)
 | 
| 825 |             self._implicitNameOp('DELETE', valuevar)
 | 
| 826 |             self.visit(node.vars)
 | 
| 827 |         self.visit(node.body)
 | 
| 828 |         self.emit('POP_BLOCK')
 | 
| 829 |         self.setups.pop()
 | 
| 830 |         self.emit('LOAD_CONST', None)
 | 
| 831 |         self.nextBlock(final)
 | 
| 832 |         self.setups.push((END_FINALLY, final))
 | 
| 833 |         self.emit('WITH_CLEANUP')
 | 
| 834 |         self.emit('END_FINALLY')
 | 
| 835 |         self.setups.pop()
 | 
| 836 |         self.__with_count -= 1
 | 
| 837 | 
 | 
| 838 |     # misc
 | 
| 839 | 
 | 
| 840 |     def visitDiscard(self, node):
 | 
| 841 |         self.set_lineno(node)
 | 
| 842 |         self.visit(node.expr)
 | 
| 843 |         self.emit('POP_TOP')
 | 
| 844 | 
 | 
| 845 |     def visitConst(self, node):
 | 
| 846 |         self.emit('LOAD_CONST', node.value)
 | 
| 847 | 
 | 
| 848 |     def visitKeyword(self, node):
 | 
| 849 |         self.emit('LOAD_CONST', node.name)
 | 
| 850 |         self.visit(node.expr)
 | 
| 851 | 
 | 
| 852 |     def visitGlobal(self, node):
 | 
| 853 |         # no code to generate
 | 
| 854 |         pass
 | 
| 855 | 
 | 
| 856 |     def visitName(self, node):
 | 
| 857 |         self.set_lineno(node)
 | 
| 858 |         self.loadName(node.name)
 | 
| 859 | 
 | 
| 860 |     def visitPass(self, node):
 | 
| 861 |         self.set_lineno(node)
 | 
| 862 | 
 | 
| 863 |     def visitImport(self, node):
 | 
| 864 |         self.set_lineno(node)
 | 
| 865 |         level = 0 if self.frame.checkFlag(CO_FUTURE_ABSIMPORT) else -1
 | 
| 866 |         for name, alias in node.names:
 | 
| 867 |             self.emit('LOAD_CONST', level)
 | 
| 868 |             self.emit('LOAD_CONST', None)
 | 
| 869 |             self.emit('IMPORT_NAME', name)
 | 
| 870 |             mod = name.split(".")[0]
 | 
| 871 |             if alias:
 | 
| 872 |                 self._resolveDots(name)
 | 
| 873 |                 self.storeName(alias)
 | 
| 874 |             else:
 | 
| 875 |                 self.storeName(mod)
 | 
| 876 | 
 | 
| 877 |     def visitFrom(self, node):
 | 
| 878 |         self.set_lineno(node)
 | 
| 879 |         level = node.level
 | 
| 880 |         if level == 0 and not self.frame.checkFlag(CO_FUTURE_ABSIMPORT):
 | 
| 881 |             level = -1
 | 
| 882 |         fromlist = tuple(name for (name, alias) in node.names)
 | 
| 883 |         self.emit('LOAD_CONST', level)
 | 
| 884 |         self.emit('LOAD_CONST', fromlist)
 | 
| 885 |         self.emit('IMPORT_NAME', node.modname)
 | 
| 886 |         for name, alias in node.names:
 | 
| 887 |             if name == '*':
 | 
| 888 |                 self.namespace = 0
 | 
| 889 |                 self.emit('IMPORT_STAR')
 | 
| 890 |                 # There can only be one name w/ from ... import *
 | 
| 891 |                 assert len(node.names) == 1
 | 
| 892 |                 return
 | 
| 893 |             else:
 | 
| 894 |                 self.emit('IMPORT_FROM', name)
 | 
| 895 |                 self._resolveDots(name)
 | 
| 896 |                 self.storeName(alias or name)
 | 
| 897 |         self.emit('POP_TOP')
 | 
| 898 | 
 | 
| 899 |     def _resolveDots(self, name):
 | 
| 900 |         elts = name.split(".")
 | 
| 901 |         if len(elts) == 1:
 | 
| 902 |             return
 | 
| 903 |         for elt in elts[1:]:
 | 
| 904 |             self.emit('LOAD_ATTR', elt)
 | 
| 905 | 
 | 
| 906 |     def visitGetattr(self, node):
 | 
| 907 |         self.visit(node.expr)
 | 
| 908 |         self.emit('LOAD_ATTR', self._mangle(node.attrname))
 | 
| 909 | 
 | 
| 910 |     # next five implement assignments
 | 
| 911 | 
 | 
| 912 |     def visitAssign(self, node):
 | 
| 913 |         self.set_lineno(node)
 | 
| 914 |         self.visit(node.expr)
 | 
| 915 |         dups = len(node.nodes) - 1
 | 
| 916 |         for i, elt in enumerate(node.nodes):
 | 
| 917 |             if i < dups:
 | 
| 918 |                 self.emit('DUP_TOP')
 | 
| 919 |             if isinstance(elt, ast.Node):
 | 
| 920 |                 self.visit(elt)
 | 
| 921 | 
 | 
| 922 |     def visitAssName(self, node):
 | 
| 923 |         if node.flags == 'OP_ASSIGN':
 | 
| 924 |             self.storeName(node.name)
 | 
| 925 |         elif node.flags == 'OP_DELETE':
 | 
| 926 |             self.set_lineno(node)
 | 
| 927 |             self.delName(node.name)
 | 
| 928 |         else:
 | 
| 929 |             print("oops", node.flags)
 | 
| 930 | 
 | 
| 931 |     def visitAssAttr(self, node):
 | 
| 932 |         self.visit(node.expr)
 | 
| 933 |         if node.flags == 'OP_ASSIGN':
 | 
| 934 |             self.emit('STORE_ATTR', self._mangle(node.attrname))
 | 
| 935 |         elif node.flags == 'OP_DELETE':
 | 
| 936 |             self.emit('DELETE_ATTR', self._mangle(node.attrname))
 | 
| 937 |         else:
 | 
| 938 |             print("warning: unexpected flags:", node.flags)
 | 
| 939 |             print(node)
 | 
| 940 | 
 | 
| 941 |     def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'):
 | 
| 942 |         v = OpFinder()
 | 
| 943 |         v.Dispatch(node)
 | 
| 944 | 
 | 
| 945 |         if v.op != 'OP_DELETE':
 | 
| 946 |             self.emit(op, len(node.nodes))
 | 
| 947 |         for child in node.nodes:
 | 
| 948 |             self.visit(child)
 | 
| 949 | 
 | 
| 950 |     visitAssTuple = _visitAssSequence
 | 
| 951 |     visitAssList = _visitAssSequence
 | 
| 952 | 
 | 
| 953 |     # augmented assignment
 | 
| 954 | 
 | 
| 955 |     def visitAugAssign(self, node):
 | 
| 956 |         self.set_lineno(node)
 | 
| 957 |         aug_node = wrap_aug(node.node)
 | 
| 958 |         self.visit(aug_node, "load")
 | 
| 959 |         self.visit(node.expr)
 | 
| 960 |         self.emit(_AUGMENTED_OPCODE[node.op])
 | 
| 961 |         self.visit(aug_node, "store")
 | 
| 962 | 
 | 
| 963 |     def visitAugName(self, node, mode):
 | 
| 964 |         if mode == "load":
 | 
| 965 |             self.loadName(node.name)
 | 
| 966 |         elif mode == "store":
 | 
| 967 |             self.storeName(node.name)
 | 
| 968 | 
 | 
| 969 |     def visitAugGetattr(self, node, mode):
 | 
| 970 |         if mode == "load":
 | 
| 971 |             self.visit(node.expr)
 | 
| 972 |             self.emit('DUP_TOP')
 | 
| 973 |             self.emit('LOAD_ATTR', self._mangle(node.attrname))
 | 
| 974 |         elif mode == "store":
 | 
| 975 |             self.emit('ROT_TWO')
 | 
| 976 |             self.emit('STORE_ATTR', self._mangle(node.attrname))
 | 
| 977 | 
 | 
| 978 |     def visitAugSlice(self, node, mode):
 | 
| 979 |         if mode == "load":
 | 
| 980 |             self.visitSlice(node, 1)
 | 
| 981 |         elif mode == "store":
 | 
| 982 |             slice = 0
 | 
| 983 |             if node.lower:
 | 
| 984 |                 slice = slice | 1
 | 
| 985 |             if node.upper:
 | 
| 986 |                 slice = slice | 2
 | 
| 987 |             if slice == 0:
 | 
| 988 |                 self.emit('ROT_TWO')
 | 
| 989 |             elif slice == 3:
 | 
| 990 |                 self.emit('ROT_FOUR')
 | 
| 991 |             else:
 | 
| 992 |                 self.emit('ROT_THREE')
 | 
| 993 |             self.emit('STORE_SLICE+%d' % slice)
 | 
| 994 | 
 | 
| 995 |     def visitAugSubscript(self, node, mode):
 | 
| 996 |         if mode == "load":
 | 
| 997 |             self.visitSubscript(node, 1)
 | 
| 998 |         elif mode == "store":
 | 
| 999 |             self.emit('ROT_THREE')
 | 
| 1000 |             self.emit('STORE_SUBSCR')
 | 
| 1001 | 
 | 
| 1002 |     def visitExec(self, node):
 | 
| 1003 |         self.visit(node.expr)
 | 
| 1004 |         if node.locals is None:
 | 
| 1005 |             self.emit('LOAD_CONST', None)
 | 
| 1006 |         else:
 | 
| 1007 |             self.visit(node.locals)
 | 
| 1008 |         if node.globals is None:
 | 
| 1009 |             self.emit('DUP_TOP')
 | 
| 1010 |         else:
 | 
| 1011 |             self.visit(node.globals)
 | 
| 1012 |         self.emit('EXEC_STMT')
 | 
| 1013 | 
 | 
| 1014 |     def visitCallFunc(self, node):
 | 
| 1015 |         pos = 0
 | 
| 1016 |         kw = 0
 | 
| 1017 |         self.set_lineno(node)
 | 
| 1018 |         self.visit(node.node)
 | 
| 1019 |         for arg in node.args:
 | 
| 1020 |             self.visit(arg)
 | 
| 1021 |             if isinstance(arg, ast.Keyword):
 | 
| 1022 |                 kw = kw + 1
 | 
| 1023 |             else:
 | 
| 1024 |                 pos = pos + 1
 | 
| 1025 |         if node.star_args is not None:
 | 
| 1026 |             self.visit(node.star_args)
 | 
| 1027 |         if node.dstar_args is not None:
 | 
| 1028 |             self.visit(node.dstar_args)
 | 
| 1029 |         have_star = node.star_args is not None
 | 
| 1030 |         have_dstar = node.dstar_args is not None
 | 
| 1031 |         opcode = _CALLFUNC_OPCODE_INFO[have_star, have_dstar]
 | 
| 1032 |         self.emit(opcode, kw << 8 | pos)
 | 
| 1033 | 
 | 
| 1034 |     def visitPrint(self, node, newline=0):
 | 
| 1035 |         self.set_lineno(node)
 | 
| 1036 |         if node.dest:
 | 
| 1037 |             self.visit(node.dest)
 | 
| 1038 |         for child in node.nodes:
 | 
| 1039 |             if node.dest:
 | 
| 1040 |                 self.emit('DUP_TOP')
 | 
| 1041 |             self.visit(child)
 | 
| 1042 |             if node.dest:
 | 
| 1043 |                 self.emit('ROT_TWO')
 | 
| 1044 |                 self.emit('PRINT_ITEM_TO')
 | 
| 1045 |             else:
 | 
| 1046 |                 self.emit('PRINT_ITEM')
 | 
| 1047 |         if node.dest and not newline:
 | 
| 1048 |             self.emit('POP_TOP')
 | 
| 1049 | 
 | 
| 1050 |     def visitPrintnl(self, node):
 | 
| 1051 |         self.visitPrint(node, newline=1)
 | 
| 1052 |         if node.dest:
 | 
| 1053 |             self.emit('PRINT_NEWLINE_TO')
 | 
| 1054 |         else:
 | 
| 1055 |             self.emit('PRINT_NEWLINE')
 | 
| 1056 | 
 | 
| 1057 |     def visitReturn(self, node):
 | 
| 1058 |         self.set_lineno(node)
 | 
| 1059 |         self.visit(node.value)
 | 
| 1060 |         self.emit('RETURN_VALUE')
 | 
| 1061 | 
 | 
| 1062 |     def visitYield(self, node):
 | 
| 1063 |         self.set_lineno(node)
 | 
| 1064 |         self.visit(node.value)
 | 
| 1065 |         self.emit('YIELD_VALUE')
 | 
| 1066 | 
 | 
| 1067 |     # slice and subscript stuff
 | 
| 1068 | 
 | 
| 1069 |     def visitSlice(self, node, aug_flag=None):
 | 
| 1070 |         # aug_flag is used by visitAugSlice
 | 
| 1071 |         self.visit(node.expr)
 | 
| 1072 |         slice = 0
 | 
| 1073 |         if node.lower:
 | 
| 1074 |             self.visit(node.lower)
 | 
| 1075 |             slice = slice | 1
 | 
| 1076 |         if node.upper:
 | 
| 1077 |             self.visit(node.upper)
 | 
| 1078 |             slice = slice | 2
 | 
| 1079 |         if aug_flag:
 | 
| 1080 |             if slice == 0:
 | 
| 1081 |                 self.emit('DUP_TOP')
 | 
| 1082 |             elif slice == 3:
 | 
| 1083 |                 self.emit('DUP_TOPX', 3)
 | 
| 1084 |             else:
 | 
| 1085 |                 self.emit('DUP_TOPX', 2)
 | 
| 1086 |         if node.flags == 'OP_APPLY':
 | 
| 1087 |             self.emit('SLICE+%d' % slice)
 | 
| 1088 |         elif node.flags == 'OP_ASSIGN':
 | 
| 1089 |             self.emit('STORE_SLICE+%d' % slice)
 | 
| 1090 |         elif node.flags == 'OP_DELETE':
 | 
| 1091 |             self.emit('DELETE_SLICE+%d' % slice)
 | 
| 1092 |         else:
 | 
| 1093 |             print("weird slice", node.flags)
 | 
| 1094 |             raise
 | 
| 1095 | 
 | 
| 1096 |     def visitSubscript(self, node, aug_flag=None):
 | 
| 1097 |         self.visit(node.expr)
 | 
| 1098 |         for sub in node.subs:
 | 
| 1099 |             self.visit(sub)
 | 
| 1100 |         if len(node.subs) > 1:
 | 
| 1101 |             self.emit('BUILD_TUPLE', len(node.subs))
 | 
| 1102 |         if aug_flag:
 | 
| 1103 |             self.emit('DUP_TOPX', 2)
 | 
| 1104 |         if node.flags == 'OP_APPLY':
 | 
| 1105 |             self.emit('BINARY_SUBSCR')
 | 
| 1106 |         elif node.flags == 'OP_ASSIGN':
 | 
| 1107 |             self.emit('STORE_SUBSCR')
 | 
| 1108 |         elif node.flags == 'OP_DELETE':
 | 
| 1109 |             self.emit('DELETE_SUBSCR')
 | 
| 1110 | 
 | 
| 1111 |     # binary ops
 | 
| 1112 | 
 | 
| 1113 |     def binaryOp(self, node, op):
 | 
| 1114 |         self.visit(node.left)
 | 
| 1115 |         self.visit(node.right)
 | 
| 1116 |         self.emit(op)
 | 
| 1117 | 
 | 
| 1118 |     def visitAdd(self, node):
 | 
| 1119 |         return self.binaryOp(node, 'BINARY_ADD')
 | 
| 1120 | 
 | 
| 1121 |     def visitSub(self, node):
 | 
| 1122 |         return self.binaryOp(node, 'BINARY_SUBTRACT')
 | 
| 1123 | 
 | 
| 1124 |     def visitMul(self, node):
 | 
| 1125 |         return self.binaryOp(node, 'BINARY_MULTIPLY')
 | 
| 1126 | 
 | 
| 1127 |     def visitDiv(self, node):
 | 
| 1128 |         return self.binaryOp(node, self._div_op)
 | 
| 1129 | 
 | 
| 1130 |     def visitFloorDiv(self, node):
 | 
| 1131 |         return self.binaryOp(node, 'BINARY_FLOOR_DIVIDE')
 | 
| 1132 | 
 | 
| 1133 |     def visitMod(self, node):
 | 
| 1134 |         return self.binaryOp(node, 'BINARY_MODULO')
 | 
| 1135 | 
 | 
| 1136 |     def visitPower(self, node):
 | 
| 1137 |         return self.binaryOp(node, 'BINARY_POWER')
 | 
| 1138 | 
 | 
| 1139 |     def visitLeftShift(self, node):
 | 
| 1140 |         return self.binaryOp(node, 'BINARY_LSHIFT')
 | 
| 1141 | 
 | 
| 1142 |     def visitRightShift(self, node):
 | 
| 1143 |         return self.binaryOp(node, 'BINARY_RSHIFT')
 | 
| 1144 | 
 | 
| 1145 |     # unary ops
 | 
| 1146 | 
 | 
| 1147 |     def unaryOp(self, node, op):
 | 
| 1148 |         self.visit(node.expr)
 | 
| 1149 |         self.emit(op)
 | 
| 1150 | 
 | 
| 1151 |     def visitInvert(self, node):
 | 
| 1152 |         return self.unaryOp(node, 'UNARY_INVERT')
 | 
| 1153 | 
 | 
| 1154 |     def visitUnarySub(self, node):
 | 
| 1155 |         return self.unaryOp(node, 'UNARY_NEGATIVE')
 | 
| 1156 | 
 | 
| 1157 |     def visitUnaryAdd(self, node):
 | 
| 1158 |         return self.unaryOp(node, 'UNARY_POSITIVE')
 | 
| 1159 | 
 | 
| 1160 |     def visitUnaryInvert(self, node):
 | 
| 1161 |         return self.unaryOp(node, 'UNARY_INVERT')
 | 
| 1162 | 
 | 
| 1163 |     def visitNot(self, node):
 | 
| 1164 |         return self.unaryOp(node, 'UNARY_NOT')
 | 
| 1165 | 
 | 
| 1166 |     def visitBackquote(self, node):
 | 
| 1167 |         return self.unaryOp(node, 'UNARY_CONVERT')
 | 
| 1168 | 
 | 
| 1169 |     # bit ops
 | 
| 1170 | 
 | 
| 1171 |     def bitOp(self, nodes, op):
 | 
| 1172 |         self.visit(nodes[0])
 | 
| 1173 |         for node in nodes[1:]:
 | 
| 1174 |             self.visit(node)
 | 
| 1175 |             self.emit(op)
 | 
| 1176 | 
 | 
| 1177 |     def visitBitand(self, node):
 | 
| 1178 |         return self.bitOp(node.nodes, 'BINARY_AND')
 | 
| 1179 | 
 | 
| 1180 |     def visitBitor(self, node):
 | 
| 1181 |         return self.bitOp(node.nodes, 'BINARY_OR')
 | 
| 1182 | 
 | 
| 1183 |     def visitBitxor(self, node):
 | 
| 1184 |         return self.bitOp(node.nodes, 'BINARY_XOR')
 | 
| 1185 | 
 | 
| 1186 |     # object constructors
 | 
| 1187 | 
 | 
| 1188 |     def visitEllipsis(self, node):
 | 
| 1189 |         self.emit('LOAD_CONST', Ellipsis)
 | 
| 1190 | 
 | 
| 1191 |     def visitTuple(self, node):
 | 
| 1192 |         self.set_lineno(node)
 | 
| 1193 |         for elt in node.nodes:
 | 
| 1194 |             self.visit(elt)
 | 
| 1195 |         self.emit('BUILD_TUPLE', len(node.nodes))
 | 
| 1196 | 
 | 
| 1197 |     def visitList(self, node):
 | 
| 1198 |         self.set_lineno(node)
 | 
| 1199 |         for elt in node.nodes:
 | 
| 1200 |             self.visit(elt)
 | 
| 1201 |         self.emit('BUILD_LIST', len(node.nodes))
 | 
| 1202 | 
 | 
| 1203 |     def visitSet(self, node):
 | 
| 1204 |         self.set_lineno(node)
 | 
| 1205 |         for elt in node.nodes:
 | 
| 1206 |             self.visit(elt)
 | 
| 1207 |         self.emit('BUILD_SET', len(node.nodes))
 | 
| 1208 | 
 | 
| 1209 |     def visitSliceobj(self, node):
 | 
| 1210 |         for child in node.nodes:
 | 
| 1211 |             self.visit(child)
 | 
| 1212 |         self.emit('BUILD_SLICE', len(node.nodes))
 | 
| 1213 | 
 | 
| 1214 |     def visitDict(self, node):
 | 
| 1215 |         self.set_lineno(node)
 | 
| 1216 |         self.emit('BUILD_MAP', 0)
 | 
| 1217 |         for k, v in node.items:
 | 
| 1218 |             self.emit('DUP_TOP')
 | 
| 1219 |             self.visit(k)
 | 
| 1220 |             self.visit(v)
 | 
| 1221 |             self.emit('ROT_THREE')
 | 
| 1222 |             self.emit('STORE_SUBSCR')
 | 
| 1223 | 
 | 
| 1224 | 
 | 
| 1225 | class TopLevelCodeGenerator(CodeGenerator):
 | 
| 1226 | 
 | 
| 1227 |     def Finish(self):
 | 
| 1228 |         pass
 | 
| 1229 | 
 | 
| 1230 | 
 | 
| 1231 | class InteractiveCodeGenerator(TopLevelCodeGenerator):
 | 
| 1232 | 
 | 
| 1233 |     def visitDiscard(self, node):
 | 
| 1234 |         # XXX Discard means it's an expression.  Perhaps this is a bad
 | 
| 1235 |         # name.
 | 
| 1236 |         self.visit(node.expr)
 | 
| 1237 |         self.emit('PRINT_EXPR')
 | 
| 1238 | 
 | 
| 1239 |     def Finish(self):
 | 
| 1240 |         # Not sure why I need this?
 | 
| 1241 |         self.emit('RETURN_VALUE')
 | 
| 1242 | 
 | 
| 1243 | 
 | 
| 1244 | # NOTE: This feature removed in Python 3!  I didn't even know about it!
 | 
| 1245 | #
 | 
| 1246 | # https://www.python.org/dev/peps/pep-3113/
 | 
| 1247 | # def fxn(a, (b, c), d):
 | 
| 1248 | #    pass
 | 
| 1249 | 
 | 
| 1250 | def _CheckNoTupleArgs(func):
 | 
| 1251 |     for i, arg in enumerate(func.argnames):
 | 
| 1252 |         if isinstance(arg, tuple):
 | 
| 1253 |             raise RuntimeError(
 | 
| 1254 |                 'Tuple args are not supported: %s in function %s' %
 | 
| 1255 |                 (arg, func.name))
 | 
| 1256 | 
 | 
| 1257 | 
 | 
| 1258 | class _FunctionCodeGenerator(CodeGenerator):
 | 
| 1259 |     """Abstract class."""
 | 
| 1260 | 
 | 
| 1261 |     def __init__(self, ctx, frame, graph, func, class_name):
 | 
| 1262 |         CodeGenerator.__init__(self, ctx, frame, graph)
 | 
| 1263 |         self.func = func
 | 
| 1264 |         self.class_name = class_name
 | 
| 1265 | 
 | 
| 1266 |         self.scope = self.ctx.scopes[func]
 | 
| 1267 | 
 | 
| 1268 |     def _optimized(self):
 | 
| 1269 |         # -fast-ops=1 is the default
 | 
| 1270 |         return self.ctx.comp_opt.fast_ops
 | 
| 1271 | 
 | 
| 1272 |     def FindLocals(self):
 | 
| 1273 |         func = self.func
 | 
| 1274 | 
 | 
| 1275 |         lnf = LocalNameFinder(set(self.func.argnames))
 | 
| 1276 |         lnf.Dispatch(func.code)
 | 
| 1277 |         self.locals.push(lnf.getLocals())
 | 
| 1278 | 
 | 
| 1279 |         if func.varargs:
 | 
| 1280 |             self.frame.setFlag(CO_VARARGS)
 | 
| 1281 |         if func.kwargs:
 | 
| 1282 |             self.frame.setFlag(CO_VARKEYWORDS)
 | 
| 1283 |         self.set_lineno(func)
 | 
| 1284 | 
 | 
| 1285 | 
 | 
| 1286 | class FunctionCodeGenerator(_FunctionCodeGenerator):
 | 
| 1287 | 
 | 
| 1288 |     def _Start(self):
 | 
| 1289 |         if self.scope.generator is not None:  # does it have yield in it?
 | 
| 1290 |             self.frame.setFlag(CO_GENERATOR)
 | 
| 1291 |         if self.func.doc:
 | 
| 1292 |             self.frame.setDocstring(self.func.doc)
 | 
| 1293 | 
 | 
| 1294 |     def Finish(self):
 | 
| 1295 |         self.graph.startExitBlock()
 | 
| 1296 |         self.emit('LOAD_CONST', None)
 | 
| 1297 |         self.emit('RETURN_VALUE')
 | 
| 1298 | 
 | 
| 1299 | 
 | 
| 1300 | class LambdaCodeGenerator(_FunctionCodeGenerator):
 | 
| 1301 | 
 | 
| 1302 |     def _Start(self):
 | 
| 1303 |         if self.scope.generator is not None:  # does it have yield in it?
 | 
| 1304 |             self.frame.setFlag(CO_GENERATOR)
 | 
| 1305 | 
 | 
| 1306 |     def Finish(self):
 | 
| 1307 |         self.graph.startExitBlock()
 | 
| 1308 |         self.emit('RETURN_VALUE')
 | 
| 1309 | 
 | 
| 1310 | 
 | 
| 1311 | class GenExprCodeGenerator(_FunctionCodeGenerator):
 | 
| 1312 | 
 | 
| 1313 |     def _Start(self):
 | 
| 1314 |         self.frame.setFlag(CO_GENERATOR)  # It's always a generator
 | 
| 1315 | 
 | 
| 1316 |     def Finish(self):
 | 
| 1317 |         self.graph.startExitBlock()
 | 
| 1318 |         self.emit('RETURN_VALUE')
 | 
| 1319 | 
 | 
| 1320 | 
 | 
| 1321 | class ClassCodeGenerator(CodeGenerator):
 | 
| 1322 | 
 | 
| 1323 |     def __init__(self, ctx, frame, graph, klass):
 | 
| 1324 |         CodeGenerator.__init__(self, ctx, frame, graph)
 | 
| 1325 |         self.klass = klass
 | 
| 1326 | 
 | 
| 1327 |         self.class_name = klass.name
 | 
| 1328 |         self.scope = self.ctx.scopes[klass]
 | 
| 1329 | 
 | 
| 1330 |     def _Start(self):
 | 
| 1331 |         self.set_lineno(self.klass)
 | 
| 1332 |         self.emit("LOAD_GLOBAL", "__name__")
 | 
| 1333 |         self.storeName("__module__")
 | 
| 1334 |         if self.klass.doc and self.ctx.comp_opt.emit_docstring:
 | 
| 1335 |             self.emit("LOAD_CONST", self.klass.doc)
 | 
| 1336 |             self.storeName('__doc__')
 | 
| 1337 | 
 | 
| 1338 |     def FindLocals(self):
 | 
| 1339 |         lnf = LocalNameFinder()
 | 
| 1340 |         lnf.Dispatch(self.klass.code)
 | 
| 1341 |         self.locals.push(lnf.getLocals())
 | 
| 1342 | 
 | 
| 1343 |         self.frame.setFlag(CO_NEWLOCALS)
 | 
| 1344 |         if self.klass.doc:
 | 
| 1345 |             self.frame.setDocstring(self.klass.doc)
 | 
| 1346 | 
 | 
| 1347 |     def Finish(self):
 | 
| 1348 |         self.graph.startExitBlock()
 | 
| 1349 |         self.emit('LOAD_LOCALS')
 | 
| 1350 |         self.emit('RETURN_VALUE')
 | 
| 1351 | 
 | 
| 1352 | 
 | 
| 1353 | class OpFinder(ASTVisitor):
 | 
| 1354 |     """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
 | 
| 1355 | 
 | 
| 1356 |     def __init__(self):
 | 
| 1357 |         ASTVisitor.__init__(self)
 | 
| 1358 |         self.op = None
 | 
| 1359 | 
 | 
| 1360 |     def visitAssName(self, node):
 | 
| 1361 |         if self.op is None:
 | 
| 1362 |             self.op = node.flags
 | 
| 1363 |         elif self.op != node.flags:
 | 
| 1364 |             raise ValueError, "mixed ops in stmt"
 | 
| 1365 | 
 | 
| 1366 |     visitAssAttr = visitAssName
 | 
| 1367 |     visitSubscript = visitAssName
 | 
| 1368 | 
 | 
| 1369 | 
 | 
| 1370 | # TODO: I don't understand this.  It looks like it does nothing, but removing
 | 
| 1371 | # wrap_aug() breaks.
 | 
| 1372 | class Delegator(object):
 | 
| 1373 |     """Base class to support delegation for augmented assignment nodes
 | 
| 1374 | 
 | 
| 1375 |     To generator code for augmented assignments, we use the following
 | 
| 1376 |     wrapper classes.  In visitAugAssign, the left-hand expression node
 | 
| 1377 |     is visited twice.  The first time the visit uses the normal method
 | 
| 1378 |     for that node .  The second time the visit uses a different method
 | 
| 1379 |     that generates the appropriate code to perform the assignment.
 | 
| 1380 |     These delegator classes wrap the original AST nodes in order to
 | 
| 1381 |     support the variant visit methods.
 | 
| 1382 |     """
 | 
| 1383 |     def __init__(self, obj):
 | 
| 1384 |         self.obj = obj
 | 
| 1385 | 
 | 
| 1386 |     def __getattr__(self, attr):
 | 
| 1387 |         return getattr(self.obj, attr)
 | 
| 1388 | 
 | 
| 1389 | 
 | 
| 1390 | class AugGetattr(Delegator):
 | 
| 1391 |     pass
 | 
| 1392 | 
 | 
| 1393 | class AugName(Delegator):
 | 
| 1394 |     pass
 | 
| 1395 | 
 | 
| 1396 | class AugSlice(Delegator):
 | 
| 1397 |     pass
 | 
| 1398 | 
 | 
| 1399 | class AugSubscript(Delegator):
 | 
| 1400 |     pass
 | 
| 1401 | 
 | 
| 1402 | AUG_WRAPPER = {
 | 
| 1403 |     ast.Getattr: AugGetattr,
 | 
| 1404 |     ast.Name: AugName,
 | 
| 1405 |     ast.Slice: AugSlice,
 | 
| 1406 |     ast.Subscript: AugSubscript,
 | 
| 1407 |     }
 | 
| 1408 | 
 | 
| 1409 | def wrap_aug(node):
 | 
| 1410 |     return AUG_WRAPPER[node.__class__](node)
 |