7 from cStringIO
import StringIO
9 from compiler
import ast
, parse
, walk
10 from compiler
import pyassem
, misc
11 from compiler
.pyassem
import CO_VARARGS
, CO_VARKEYWORDS
, CO_NEWLOCALS
, TupleArg
13 callfunc_opcode_info
= {
14 # (Have *args, Have **args) : opcode
15 (0,0) : "CALL_FUNCTION",
16 (1,0) : "CALL_FUNCTION_VAR",
17 (0,1) : "CALL_FUNCTION_KW",
18 (1,1) : "CALL_FUNCTION_VAR_KW",
21 def compile(filename
):
25 mod
= Module(buf
, filename
)
27 f
= open(filename
+ "c", "wb")
32 def __init__(self
, source
, filename
):
33 self
.filename
= filename
38 ast
= parse(self
.source
)
39 root
, filename
= os
.path
.split(self
.filename
)
40 gen
= ModuleCodeGenerator(filename
)
42 self
.code
= gen
.getCode()
45 f
.write(self
.getPycHeader())
46 marshal
.dump(self
.code
, f
)
48 MAGIC
= (50823 |
(ord('\r')<<16) |
(ord('\n')<<24))
50 def getPycHeader(self
):
51 # compile.c uses marshal to write a long directly, with
52 # calling the interface that would also generate a 1-byte code
53 # to indicate the type of the value. simplest way to get the
54 # same effect is to call marshal and then skip the code.
55 magic
= marshal
.dumps(self
.MAGIC
)[1:]
56 mtime
= os
.stat(self
.filename
)[stat
.ST_MTIME
]
57 mtime
= struct
.pack('i', mtime
)
62 optimized
= 0 # is namespace access optimized?
64 def __init__(self
, filename
):
65 ## Subclasses must define a constructor that intializes self.graph
66 ## before calling this init function
67 ## self.graph = pyassem.PyFlowGraph()
68 self
.filename
= filename
69 self
.locals = misc
.Stack()
70 self
.loops
= misc
.Stack()
73 self
.last_lineno
= None
74 self
._setupGraphDelegation
()
76 def _setupGraphDelegation(self
):
77 self
.emit
= self
.graph
.emit
78 self
.newBlock
= self
.graph
.newBlock
79 self
.startBlock
= self
.graph
.startBlock
80 self
.nextBlock
= self
.graph
.nextBlock
81 self
.setDocstring
= self
.graph
.setDocstring
84 """Return a code object"""
85 return self
.graph
.getCode()
87 # Next five methods handle name access
89 def isLocalName(self
, name
):
90 return self
.locals.top().has_elt(name
)
92 def storeName(self
, name
):
93 self
._nameOp
('STORE', name
)
95 def loadName(self
, name
):
96 self
._nameOp
('LOAD', name
)
98 def delName(self
, name
):
99 self
._nameOp
('DELETE', name
)
101 def _nameOp(self
, prefix
, name
):
102 if not self
.optimized
:
103 self
.emit(prefix
+ '_NAME', name
)
105 if self
.isLocalName(name
):
106 self
.emit(prefix
+ '_FAST', name
)
108 self
.emit(prefix
+ '_GLOBAL', name
)
110 def set_lineno(self
, node
):
111 """Emit SET_LINENO if node has lineno attribute and it is
112 different than the last lineno emitted.
114 Returns true if SET_LINENO was emitted.
116 There are no rules for when an AST node should have a lineno
117 attribute. The transformer and AST code need to be reviewed
118 and a consistent policy implemented and documented. Until
119 then, this method works around missing line numbers.
121 lineno
= getattr(node
, 'lineno', None)
122 if lineno
is not None and lineno
!= self
.last_lineno
:
123 self
.emit('SET_LINENO', lineno
)
124 self
.last_lineno
= lineno
128 # The first few visitor methods handle nodes that generator new
131 def visitModule(self
, node
):
132 lnf
= walk(node
.node
, LocalNameFinder(), 0)
133 self
.locals.push(lnf
.getLocals())
134 self
.setDocstring(node
.doc
)
135 self
.visit(node
.node
)
136 self
.emit('LOAD_CONST', None)
137 self
.emit('RETURN_VALUE')
139 def visitFunction(self
, node
):
140 self
._visitFuncOrLambda
(node
, isLambda
=0)
141 self
.storeName(node
.name
)
143 def visitLambda(self
, node
):
144 self
._visitFuncOrLambda
(node
, isLambda
=1)
145 ## self.storeName("<lambda>")
147 def _visitFuncOrLambda(self
, node
, isLambda
):
148 gen
= FunctionCodeGenerator(node
, self
.filename
, isLambda
)
151 self
.set_lineno(node
)
152 for default
in node
.defaults
:
154 self
.emit('LOAD_CONST', gen
.getCode())
155 self
.emit('MAKE_FUNCTION', len(node
.defaults
))
157 def visitClass(self
, node
):
158 gen
= ClassCodeGenerator(node
, self
.filename
)
161 self
.set_lineno(node
)
162 self
.emit('LOAD_CONST', node
.name
)
163 for base
in node
.bases
:
165 self
.emit('BUILD_TUPLE', len(node
.bases
))
166 self
.emit('LOAD_CONST', gen
.getCode())
167 self
.emit('MAKE_FUNCTION', 0)
168 self
.emit('CALL_FUNCTION', 0)
169 self
.emit('BUILD_CLASS')
170 self
.storeName(node
.name
)
172 # The rest are standard visitor methods
174 # The next few implement control-flow statements
176 def visitIf(self
, node
):
177 end
= self
.newBlock()
178 numtests
= len(node
.tests
)
179 for i
in range(numtests
):
180 test
, suite
= node
.tests
[i
]
181 self
.set_lineno(test
)
183 ## if i == numtests - 1 and not node.else_:
186 ## nextTest = self.newBlock()
187 nextTest
= self
.newBlock()
188 self
.emit('JUMP_IF_FALSE', nextTest
)
192 self
.emit('JUMP_FORWARD', end
)
193 self
.nextBlock(nextTest
)
196 self
.visit(node
.else_
)
199 def visitWhile(self
, node
):
200 self
.set_lineno(node
)
202 loop
= self
.newBlock()
203 else_
= self
.newBlock()
205 after
= self
.newBlock()
206 self
.emit('SETUP_LOOP', after
)
209 self
.loops
.push(loop
)
211 self
.set_lineno(node
)
212 self
.visit(node
.test
)
213 self
.emit('JUMP_IF_FALSE', else_
or after
)
217 self
.visit(node
.body
)
218 self
.emit('JUMP_ABSOLUTE', loop
)
220 self
.startBlock(else_
) # or just the POPs if not else clause
222 self
.emit('POP_BLOCK')
224 self
.visit(node
.else_
)
226 self
.nextBlock(after
)
228 def visitFor(self
, node
):
229 start
= self
.newBlock()
230 anchor
= self
.newBlock()
231 after
= self
.newBlock()
232 self
.loops
.push(start
)
234 self
.set_lineno(node
)
235 self
.emit('SETUP_LOOP', after
)
236 self
.visit(node
.list)
237 self
.visit(ast
.Const(0))
238 self
.nextBlock(start
)
239 self
.set_lineno(node
)
240 self
.emit('FOR_LOOP', anchor
)
241 self
.visit(node
.assign
)
242 self
.visit(node
.body
)
243 self
.emit('JUMP_ABSOLUTE', start
)
244 self
.nextBlock(anchor
)
245 self
.emit('POP_BLOCK')
247 self
.visit(node
.else_
)
249 self
.nextBlock(after
)
251 def visitBreak(self
, node
):
253 raise SyntaxError, "'break' outside loop (%s, %d)" % \
254 (self
.filename
, node
.lineno
)
255 self
.set_lineno(node
)
256 self
.emit('BREAK_LOOP')
258 def visitContinue(self
, node
):
260 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
261 (self
.filename
, node
.lineno
)
263 self
.set_lineno(node
)
264 self
.emit('JUMP_ABSOLUTE', l
)
267 def visitTest(self
, node
, jump
):
268 end
= self
.newBlock()
269 for child
in node
.nodes
[:-1]:
274 self
.visit(node
.nodes
[-1])
277 def visitAnd(self
, node
):
278 self
.visitTest(node
, 'JUMP_IF_FALSE')
280 def visitOr(self
, node
):
281 self
.visitTest(node
, 'JUMP_IF_TRUE')
283 def visitCompare(self
, node
):
284 self
.visit(node
.expr
)
285 cleanup
= self
.newBlock()
286 for op
, code
in node
.ops
[:-1]:
289 self
.emit('ROT_THREE')
290 self
.emit('COMPARE_OP', op
)
291 self
.emit('JUMP_IF_FALSE', cleanup
)
294 # now do the last comparison
296 op
, code
= node
.ops
[-1]
298 self
.emit('COMPARE_OP', op
)
299 if len(node
.ops
) > 1:
300 end
= self
.newBlock()
301 self
.emit('JUMP_FORWARD', end
)
302 self
.nextBlock(cleanup
)
309 def visitAssert(self
, node
):
310 # XXX would be interesting to implement this via a
311 # transformation of the AST before this stage
312 end
= self
.newBlock()
313 self
.set_lineno(node
)
314 # XXX __debug__ and AssertionError appear to be special cases
315 # -- they are always loaded as globals even if there are local
316 # names. I guess this is a sort of renaming op.
317 self
.emit('LOAD_GLOBAL', '__debug__')
318 self
.emit('JUMP_IF_FALSE', end
)
321 self
.visit(node
.test
)
322 self
.emit('JUMP_IF_TRUE', end
)
324 self
.emit('LOAD_GLOBAL', 'AssertionError')
325 self
.visit(node
.fail
)
326 self
.emit('RAISE_VARARGS', 2)
330 def visitRaise(self
, node
):
331 self
.set_lineno(node
)
334 self
.visit(node
.expr1
)
337 self
.visit(node
.expr2
)
340 self
.visit(node
.expr3
)
342 self
.emit('RAISE_VARARGS', n
)
344 def visitTryExcept(self
, node
):
345 handlers
= self
.newBlock()
346 end
= self
.newBlock()
348 lElse
= self
.newBlock()
351 self
.set_lineno(node
)
352 self
.emit('SETUP_EXCEPT', handlers
)
353 self
.visit(node
.body
)
354 self
.emit('POP_BLOCK')
355 self
.emit('JUMP_FORWARD', lElse
)
356 self
.nextBlock(handlers
)
358 last
= len(node
.handlers
) - 1
359 for i
in range(len(node
.handlers
)):
360 expr
, target
, body
= node
.handlers
[i
]
361 self
.set_lineno(expr
)
365 self
.emit('COMPARE_OP', 'exception match')
366 next
= self
.newBlock()
367 self
.emit('JUMP_IF_FALSE', next
)
377 self
.emit('JUMP_FORWARD', end
)
381 self
.emit('END_FINALLY')
383 self
.nextBlock(lElse
)
384 self
.visit(node
.else_
)
387 def visitTryFinally(self
, node
):
388 final
= self
.newBlock()
389 self
.set_lineno(node
)
390 self
.emit('SETUP_FINALLY', final
)
391 self
.visit(node
.body
)
392 self
.emit('POP_BLOCK')
393 self
.emit('LOAD_CONST', None)
394 self
.nextBlock(final
)
395 self
.visit(node
.final
)
396 self
.emit('END_FINALLY')
400 ## def visitStmt(self, node):
401 ## # nothing to do except walk the children
404 def visitDiscard(self
, node
):
405 self
.visit(node
.expr
)
408 def visitConst(self
, node
):
409 self
.emit('LOAD_CONST', node
.value
)
411 def visitKeyword(self
, node
):
412 self
.emit('LOAD_CONST', node
.name
)
413 self
.visit(node
.expr
)
415 def visitGlobal(self
, node
):
416 # no code to generate
419 def visitName(self
, node
):
420 self
.set_lineno(node
)
421 self
.loadName(node
.name
)
423 def visitPass(self
, node
):
424 self
.set_lineno(node
)
426 def visitImport(self
, node
):
427 self
.set_lineno(node
)
428 for name
, alias
in node
.names
:
429 self
.emit('LOAD_CONST', None)
430 self
.emit('IMPORT_NAME', name
)
431 self
._resolveDots
(name
)
432 self
.storeName(alias
or name
)
434 def visitFrom(self
, node
):
435 self
.set_lineno(node
)
436 fromlist
= map(lambda (name
, alias
): name
, node
.names
)
437 self
.emit('LOAD_CONST', tuple(fromlist
))
438 self
.emit('IMPORT_NAME', node
.modname
)
439 for name
, alias
in node
.names
:
442 self
.emit('IMPORT_FROM', name
)
443 self
._resolveDots
(name
)
444 self
.storeName(alias
or name
)
447 def _resolveDots(self
, name
):
448 elts
= string
.split(name
, ".")
452 self
.emit('LOAD_ATTR', elt
)
454 def visitGetattr(self
, node
):
455 self
.visit(node
.expr
)
456 self
.emit('LOAD_ATTR', node
.attrname
)
458 # next five implement assignments
460 def visitAssign(self
, node
):
461 self
.set_lineno(node
)
462 self
.visit(node
.expr
)
463 dups
= len(node
.nodes
) - 1
464 for i
in range(len(node
.nodes
)):
468 if isinstance(elt
, ast
.Node
):
471 def visitAssName(self
, node
):
472 if node
.flags
== 'OP_ASSIGN':
473 self
.storeName(node
.name
)
474 elif node
.flags
== 'OP_DELETE':
475 self
.delName(node
.name
)
477 print "oops", node
.flags
479 def visitAssAttr(self
, node
):
480 self
.visit(node
.expr
)
481 if node
.flags
== 'OP_ASSIGN':
482 self
.emit('STORE_ATTR', node
.attrname
)
483 elif node
.flags
== 'OP_DELETE':
484 self
.emit('DELETE_ATTR', node
.attrname
)
486 print "warning: unexpected flags:", node
.flags
489 def visitAssTuple(self
, node
):
490 if findOp(node
) != 'OP_DELETE':
491 self
.emit('UNPACK_SEQUENCE', len(node
.nodes
))
492 for child
in node
.nodes
:
495 visitAssList
= visitAssTuple
497 def visitExec(self
, node
):
498 self
.visit(node
.expr
)
499 if node
.locals is None:
500 self
.emit('LOAD_CONST', None)
502 self
.visit(node
.locals)
503 if node
.globals is None:
506 self
.visit(node
.globals)
507 self
.emit('EXEC_STMT')
509 def visitCallFunc(self
, node
):
512 self
.set_lineno(node
)
513 self
.visit(node
.node
)
514 for arg
in node
.args
:
516 if isinstance(arg
, ast
.Keyword
):
520 if node
.star_args
is not None:
521 self
.visit(node
.star_args
)
522 if node
.dstar_args
is not None:
523 self
.visit(node
.dstar_args
)
524 have_star
= node
.star_args
is not None
525 have_dstar
= node
.dstar_args
is not None
526 opcode
= callfunc_opcode_info
[have_star
, have_dstar
]
527 self
.emit(opcode
, kw
<< 8 | pos
)
529 def visitPrint(self
, node
):
530 self
.set_lineno(node
)
531 for child
in node
.nodes
:
533 self
.emit('PRINT_ITEM')
535 def visitPrintnl(self
, node
):
536 self
.visitPrint(node
)
537 self
.emit('PRINT_NEWLINE')
539 def visitReturn(self
, node
):
540 self
.set_lineno(node
)
541 self
.visit(node
.value
)
542 self
.emit('RETURN_VALUE')
544 # slice and subscript stuff
546 def visitSlice(self
, node
):
547 self
.visit(node
.expr
)
550 self
.visit(node
.lower
)
553 self
.visit(node
.upper
)
555 if node
.flags
== 'OP_APPLY':
556 self
.emit('SLICE+%d' % slice)
557 elif node
.flags
== 'OP_ASSIGN':
558 self
.emit('STORE_SLICE+%d' % slice)
559 elif node
.flags
== 'OP_DELETE':
560 self
.emit('DELETE_SLICE+%d' % slice)
562 print "weird slice", node
.flags
565 def visitSubscript(self
, node
):
566 self
.visit(node
.expr
)
567 for sub
in node
.subs
:
569 if len(node
.subs
) > 1:
570 self
.emit('BUILD_TUPLE', len(node
.subs
))
571 if node
.flags
== 'OP_APPLY':
572 self
.emit('BINARY_SUBSCR')
573 elif node
.flags
== 'OP_ASSIGN':
574 self
.emit('STORE_SUBSCR')
575 elif node
.flags
== 'OP_DELETE':
576 self
.emit('DELETE_SUBSCR')
580 def binaryOp(self
, node
, op
):
581 self
.visit(node
.left
)
582 self
.visit(node
.right
)
585 def visitAdd(self
, node
):
586 return self
.binaryOp(node
, 'BINARY_ADD')
588 def visitSub(self
, node
):
589 return self
.binaryOp(node
, 'BINARY_SUBTRACT')
591 def visitMul(self
, node
):
592 return self
.binaryOp(node
, 'BINARY_MULTIPLY')
594 def visitDiv(self
, node
):
595 return self
.binaryOp(node
, 'BINARY_DIVIDE')
597 def visitMod(self
, node
):
598 return self
.binaryOp(node
, 'BINARY_MODULO')
600 def visitPower(self
, node
):
601 return self
.binaryOp(node
, 'BINARY_POWER')
603 def visitLeftShift(self
, node
):
604 return self
.binaryOp(node
, 'BINARY_LSHIFT')
606 def visitRightShift(self
, node
):
607 return self
.binaryOp(node
, 'BINARY_RSHIFT')
611 def unaryOp(self
, node
, op
):
612 self
.visit(node
.expr
)
615 def visitInvert(self
, node
):
616 return self
.unaryOp(node
, 'UNARY_INVERT')
618 def visitUnarySub(self
, node
):
619 return self
.unaryOp(node
, 'UNARY_NEGATIVE')
621 def visitUnaryAdd(self
, node
):
622 return self
.unaryOp(node
, 'UNARY_POSITIVE')
624 def visitUnaryInvert(self
, node
):
625 return self
.unaryOp(node
, 'UNARY_INVERT')
627 def visitNot(self
, node
):
628 return self
.unaryOp(node
, 'UNARY_NOT')
630 def visitBackquote(self
, node
):
631 return self
.unaryOp(node
, 'UNARY_CONVERT')
635 def bitOp(self
, nodes
, op
):
637 for node
in nodes
[1:]:
641 def visitBitand(self
, node
):
642 return self
.bitOp(node
.nodes
, 'BINARY_AND')
644 def visitBitor(self
, node
):
645 return self
.bitOp(node
.nodes
, 'BINARY_OR')
647 def visitBitxor(self
, node
):
648 return self
.bitOp(node
.nodes
, 'BINARY_XOR')
650 # object constructors
652 def visitEllipsis(self
, node
):
653 self
.emit('LOAD_CONST', Ellipsis)
655 def visitTuple(self
, node
):
656 for elt
in node
.nodes
:
658 self
.emit('BUILD_TUPLE', len(node
.nodes
))
660 def visitList(self
, node
):
661 for elt
in node
.nodes
:
663 self
.emit('BUILD_LIST', len(node
.nodes
))
665 def visitSliceobj(self
, node
):
666 for child
in node
.nodes
:
668 self
.emit('BUILD_SLICE', len(node
.nodes
))
670 def visitDict(self
, node
):
671 lineno
= getattr(node
, 'lineno', None)
673 set.emit('SET_LINENO', lineno
)
674 self
.emit('BUILD_MAP', 0)
675 for k
, v
in node
.items
:
676 lineno2
= getattr(node
, 'lineno', None)
677 if lineno2
is not None and lineno
!= lineno2
:
678 self
.emit('SET_LINENO', lineno2
)
684 self
.emit('STORE_SUBSCR')
686 class ModuleCodeGenerator(CodeGenerator
):
687 super_init
= CodeGenerator
.__init
__
689 def __init__(self
, filename
):
690 # XXX <module> is ? in compile.c
691 self
.graph
= pyassem
.PyFlowGraph("<module>", filename
)
692 self
.super_init(filename
)
694 class FunctionCodeGenerator(CodeGenerator
):
695 super_init
= CodeGenerator
.__init
__
700 def __init__(self
, func
, filename
, isLambda
=0):
702 klass
= FunctionCodeGenerator
703 name
= "<lambda.%d>" % klass
.lambdaCount
704 klass
.lambdaCount
= klass
.lambdaCount
+ 1
707 args
, hasTupleArg
= generateArgList(func
.argnames
)
708 self
.graph
= pyassem
.PyFlowGraph(name
, filename
, args
,
710 self
.isLambda
= isLambda
711 self
.super_init(filename
)
713 lnf
= walk(func
.code
, LocalNameFinder(args
), 0)
714 self
.locals.push(lnf
.getLocals())
716 self
.graph
.setFlag(CO_VARARGS
)
718 self
.graph
.setFlag(CO_VARKEYWORDS
)
719 self
.set_lineno(func
)
721 self
.generateArgUnpack(func
.argnames
)
724 self
.graph
.startExitBlock()
725 if not self
.isLambda
:
726 self
.emit('LOAD_CONST', None)
727 self
.emit('RETURN_VALUE')
729 def generateArgUnpack(self
, args
):
732 if type(arg
) == types
.TupleType
:
733 self
.emit('LOAD_FAST', '.nested%d' % count
)
735 self
.unpackSequence(arg
)
737 def unpackSequence(self
, tup
):
738 self
.emit('UNPACK_SEQUENCE', len(tup
))
740 if type(elt
) == types
.TupleType
:
741 self
.unpackSequence(elt
)
743 self
.emit('STORE_FAST', elt
)
745 unpackTuple
= unpackSequence
747 class ClassCodeGenerator(CodeGenerator
):
748 super_init
= CodeGenerator
.__init
__
750 def __init__(self
, klass
, filename
):
751 self
.graph
= pyassem
.PyFlowGraph(klass
.name
, filename
,
753 self
.super_init(filename
)
754 lnf
= walk(klass
.code
, LocalNameFinder(), 0)
755 self
.locals.push(lnf
.getLocals())
756 self
.graph
.setFlag(CO_NEWLOCALS
)
759 self
.graph
.startExitBlock()
760 self
.emit('LOAD_LOCALS')
761 self
.emit('RETURN_VALUE')
764 def generateArgList(arglist
):
765 """Generate an arg list marking TupleArgs"""
770 if type(elt
) == types
.StringType
:
772 elif type(elt
) == types
.TupleType
:
773 args
.append(TupleArg(count
, elt
))
775 extra
.extend(misc
.flatten(elt
))
777 raise ValueError, "unexpect argument type:", elt
778 return args
+ extra
, count
780 class LocalNameFinder
:
781 """Find local names in scope"""
782 def __init__(self
, names
=()):
783 self
.names
= misc
.Set()
784 self
.globals = misc
.Set()
789 for elt
in self
.globals.elements():
790 if self
.names
.has_elt(elt
):
791 self
.names
.remove(elt
)
794 def visitDict(self
, node
):
797 def visitGlobal(self
, node
):
798 for name
in node
.names
:
799 self
.globals.add(name
)
801 def visitFunction(self
, node
):
802 self
.names
.add(node
.name
)
804 def visitLambda(self
, node
):
807 def visitImport(self
, node
):
808 for name
, alias
in node
.names
:
809 self
.names
.add(alias
or name
)
811 def visitFrom(self
, node
):
812 for name
, alias
in node
.names
:
813 self
.names
.add(alias
or name
)
815 def visitClass(self
, node
):
816 self
.names
.add(node
.name
)
818 def visitAssName(self
, node
):
819 self
.names
.add(node
.name
)
822 """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
830 def visitAssName(self
, node
):
833 elif self
.op
!= node
.flags
:
834 raise ValueError, "mixed ops in stmt"
836 if __name__
== "__main__":
839 for file in sys
.argv
[1:]: