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