The 0.5 release happened on 2/15, not on 2/14. :-)
[python/dscho.git] / Lib / compiler / pycodegen.py
blob9b9ee16dffa63b5a039abba82d88a94ded91d46f
1 """Python bytecode generator
3 Currently contains generic ASTVisitor code, a LocalNameFinder, and a
4 CodeGenerator. Eventually, this will get split into the ASTVisitor as
5 a generic tool and CodeGenerator as a specific tool.
6 """
8 from p2c import transformer, ast
9 from pyassem import StackRef, PyAssembler
10 import dis
11 import misc
12 import marshal
13 import new
14 import string
15 import sys
16 import os
17 import stat
18 import struct
19 import types
21 def parse(path):
22 f = open(path)
23 src = f.read()
24 f.close()
25 t = transformer.Transformer()
26 return t.parsesuite(src)
28 def walk(tree, visitor, verbose=None, walker=None):
29 if walker:
30 w = walker()
31 else:
32 w = ASTVisitor()
33 if verbose is not None:
34 w.VERBOSE = verbose
35 w.preorder(tree, visitor)
36 return w.visitor
38 def dumpNode(node):
39 print node.__class__
40 for attr in dir(node):
41 if attr[0] != '_':
42 print "\t", "%-10.10s" % attr, getattr(node, attr)
44 class ASTVisitor:
45 """Performs a depth-first walk of the AST
47 The ASTVisitor will walk the AST, performing either a preorder or
48 postorder traversal depending on which method is called.
50 methods:
51 preorder(tree, visitor)
52 postorder(tree, visitor)
53 tree: an instance of ast.Node
54 visitor: an instance with visitXXX methods
56 The ASTVisitor is responsible for walking over the tree in the
57 correct order. For each node, it checks the visitor argument for
58 a method named 'visitNodeType' where NodeType is the name of the
59 node's class, e.g. Classdef. If the method exists, it is called
60 with the node as its sole argument.
62 The visitor method for a particular node type can control how
63 child nodes are visited during a preorder walk. (It can't control
64 the order during a postorder walk, because it is called _after_
65 the walk has occurred.) The ASTVisitor modifies the visitor
66 argument by adding a visit method to the visitor; this method can
67 be used to visit a particular child node. If the visitor method
68 returns a true value, the ASTVisitor will not traverse the child
69 nodes.
71 XXX The interface for controlling the preorder walk needs to be
72 re-considered. The current interface is convenient for visitors
73 that mostly let the ASTVisitor do everything. For something like
74 a code generator, where you want to walk to occur in a specific
75 order, it's a pain to add "return 1" to the end of each method.
77 XXX Perhaps I can use a postorder walk for the code generator?
78 """
80 VERBOSE = 0
82 def __init__(self):
83 self.node = None
85 def preorder(self, tree, visitor):
86 """Do preorder walk of tree using visitor"""
87 self.visitor = visitor
88 visitor.visit = self._preorder
89 self._preorder(tree)
91 def _preorder(self, node):
92 stop = self.dispatch(node)
93 if stop:
94 return
95 for child in node.getChildren():
96 if isinstance(child, ast.Node):
97 self._preorder(child)
99 def postorder(self, tree, visitor):
100 """Do preorder walk of tree using visitor"""
101 self.visitor = visitor
102 visitor.visit = self._postorder
103 self._postorder(tree)
105 def _postorder(self, tree):
106 for child in node.getChildren():
107 if isinstance(child, ast.Node):
108 self._preorder(child)
109 self.dispatch(node)
111 def dispatch(self, node):
112 self.node = node
113 className = node.__class__.__name__
114 meth = getattr(self.visitor, 'visit' + className, None)
115 if self.VERBOSE > 0:
116 if self.VERBOSE == 1:
117 if meth is None:
118 print "dispatch", className
119 else:
120 print "dispatch", className, (meth and meth.__name__ or '')
121 if meth:
122 return meth(node)
124 class ExampleASTVisitor(ASTVisitor):
125 """Prints examples of the nodes that aren't visited
127 This visitor-driver is only useful for development, when it's
128 helpful to develop a visitor incremently, and get feedback on what
129 you still have to do.
131 examples = {}
133 def dispatch(self, node):
134 self.node = node
135 className = node.__class__.__name__
136 meth = getattr(self.visitor, 'visit' + className, None)
137 if self.VERBOSE > 0:
138 if self.VERBOSE == 1:
139 if meth is None:
140 print "dispatch", className
141 else:
142 print "dispatch", className, (meth and meth.__name__ or '')
143 if meth:
144 return meth(node)
145 else:
146 klass = node.__class__
147 if self.VERBOSE < 2:
148 if self.examples.has_key(klass):
149 return
150 self.examples[klass] = klass
151 print
152 print klass
153 for attr in dir(node):
154 if attr[0] != '_':
155 print "\t", "%-12.12s" % attr, getattr(node, attr)
156 print
158 class CodeGenerator:
159 """Generate bytecode for the Python VM"""
161 OPTIMIZED = 1
163 # XXX should clean up initialization and generateXXX funcs
164 def __init__(self, filename="<?>"):
165 self.filename = filename
166 self.code = PyAssembler()
167 self.code.setFlags(0)
168 self.locals = misc.Stack()
169 self.loops = misc.Stack()
170 self.namespace = 0
171 self.curStack = 0
172 self.maxStack = 0
174 def emit(self, *args):
175 # XXX could just use self.emit = self.code.emit
176 apply(self.code.emit, args)
178 def _generateFunctionOrLambdaCode(self, func):
179 self.name = func.name
180 self.filename = filename
182 # keep a lookout for 'def foo((x,y)):'
183 args, hasTupleArg = self.generateArglist(func.argnames)
185 self.code = PyAssembler(args=args, name=func.name,
186 filename=filename)
187 self.namespace = self.OPTIMIZED
188 if func.varargs:
189 self.code.setVarArgs()
190 if func.kwargs:
191 self.code.setKWArgs()
192 lnf = walk(func.code, LocalNameFinder(args), 0)
193 self.locals.push(lnf.getLocals())
194 self.emit('SET_LINENO', func.lineno)
195 if hasTupleArg:
196 self.generateArgUnpack(func.argnames)
197 walk(func.code, self)
199 def generateArglist(self, arglist):
200 args = []
201 extra = []
202 count = 0
203 for elt in arglist:
204 if type(elt) == types.StringType:
205 args.append(elt)
206 elif type(elt) == types.TupleType:
207 args.append(".nested%d" % count)
208 count = count + 1
209 extra.extend(misc.flatten(elt))
210 else:
211 raise ValueError, "unexpect argument type:", elt
212 return args + extra, count
214 def generateArgUnpack(self, args):
215 count = 0
216 for arg in args:
217 if type(arg) == types.TupleType:
218 self.emit('LOAD_FAST', '.nested%d' % count)
219 count = count + 1
220 self.unpackTuple(arg)
222 def unpackTuple(self, tup):
223 self.emit('UNPACK_TUPLE', len(tup))
224 for elt in tup:
225 if type(elt) == types.TupleType:
226 self.unpackTuple(elt)
227 else:
228 self.emit('STORE_FAST', elt)
230 def generateFunctionCode(self, func):
231 """Generate code for a function body"""
232 self._generateFunctionOrLambdaCode(func)
233 self.emit('LOAD_CONST', None)
234 self.emit('RETURN_VALUE')
236 def generateLambdaCode(self, func):
237 self._generateFunctionOrLambdaCode(func)
238 self.emit('RETURN_VALUE')
240 def generateClassCode(self, klass):
241 self.code = PyAssembler(name=klass.name,
242 filename=filename)
243 self.emit('SET_LINENO', klass.lineno)
244 lnf = walk(klass.code, LocalNameFinder(), 0)
245 self.locals.push(lnf.getLocals())
246 walk(klass.code, self)
247 self.emit('LOAD_LOCALS')
248 self.emit('RETURN_VALUE')
250 def asConst(self):
251 """Create a Python code object."""
252 if self.namespace == self.OPTIMIZED:
253 self.code.setOptimized()
254 return self.code.makeCodeObject()
256 def isLocalName(self, name):
257 return self.locals.top().has_elt(name)
259 def _nameOp(self, prefix, name):
260 if self.isLocalName(name):
261 if self.namespace == self.OPTIMIZED:
262 self.emit(prefix + '_FAST', name)
263 else:
264 self.emit(prefix + '_NAME', name)
265 else:
266 self.emit(prefix + '_GLOBAL', name)
268 def storeName(self, name):
269 self._nameOp('STORE', name)
271 def loadName(self, name):
272 self._nameOp('LOAD', name)
274 def delName(self, name):
275 self._nameOp('DELETE', name)
277 def visitNULL(self, node):
278 """Method exists only to stop warning in -v mode"""
279 pass
281 visitStmt = visitNULL
282 visitGlobal = visitNULL
284 def visitDiscard(self, node):
285 self.visit(node.expr)
286 self.emit('POP_TOP')
287 return 1
289 def visitPass(self, node):
290 self.emit('SET_LINENO', node.lineno)
292 def visitModule(self, node):
293 lnf = walk(node.node, LocalNameFinder(), 0)
294 self.locals.push(lnf.getLocals())
295 self.visit(node.node)
296 self.emit('LOAD_CONST', None)
297 self.emit('RETURN_VALUE')
298 return 1
300 def visitImport(self, node):
301 self.emit('SET_LINENO', node.lineno)
302 for name in node.names:
303 self.emit('IMPORT_NAME', name)
304 self.storeName(name)
306 def visitFrom(self, node):
307 self.emit('SET_LINENO', node.lineno)
308 self.emit('IMPORT_NAME', node.modname)
309 for name in node.names:
310 if name == '*':
311 self.namespace = 0
312 self.emit('IMPORT_FROM', name)
313 self.emit('POP_TOP')
315 def visitClassdef(self, node):
316 self.emit('SET_LINENO', node.lineno)
317 self.emit('LOAD_CONST', node.name)
318 for base in node.bases:
319 self.visit(base)
320 self.emit('BUILD_TUPLE', len(node.bases))
321 classBody = CodeGenerator(self.filename)
322 classBody.generateClassCode(node)
323 self.emit('LOAD_CONST', classBody)
324 self.emit('MAKE_FUNCTION', 0)
325 self.emit('CALL_FUNCTION', 0)
326 self.emit('BUILD_CLASS')
327 self.storeName(node.name)
328 return 1
330 def _visitFuncOrLambda(self, node, kind):
331 """Code common to Function and Lambda nodes"""
332 codeBody = CodeGenerator(self.filename)
333 getattr(codeBody, 'generate%sCode' % kind)(node)
334 self.emit('SET_LINENO', node.lineno)
335 for default in node.defaults:
336 self.visit(default)
337 self.emit('LOAD_CONST', codeBody)
338 self.emit('MAKE_FUNCTION', len(node.defaults))
340 def visitFunction(self, node):
341 self._visitFuncOrLambda(node, 'Function')
342 self.storeName(node.name)
343 return 1
345 def visitLambda(self, node):
346 node.name = '<lambda>'
347 node.varargs = node.kwargs = None
348 self._visitFuncOrLambda(node, 'Lambda')
349 return 1
351 def visitCallFunc(self, node):
352 pos = 0
353 kw = 0
354 if hasattr(node, 'lineno'):
355 self.emit('SET_LINENO', node.lineno)
356 self.visit(node.node)
357 for arg in node.args:
358 self.visit(arg)
359 if isinstance(arg, ast.Keyword):
360 kw = kw + 1
361 else:
362 pos = pos + 1
363 self.emit('CALL_FUNCTION', kw << 8 | pos)
364 return 1
366 def visitKeyword(self, node):
367 self.emit('LOAD_CONST', node.name)
368 self.visit(node.expr)
369 return 1
371 def visitIf(self, node):
372 after = StackRef()
373 for test, suite in node.tests:
374 if hasattr(test, 'lineno'):
375 self.emit('SET_LINENO', test.lineno)
376 else:
377 print "warning", "no line number"
378 self.visit(test)
379 dest = StackRef()
380 self.emit('JUMP_IF_FALSE', dest)
381 self.emit('POP_TOP')
382 self.visit(suite)
383 self.emit('JUMP_FORWARD', after)
384 dest.bind(self.code.getCurInst())
385 self.emit('POP_TOP')
386 if node.else_:
387 self.visit(node.else_)
388 after.bind(self.code.getCurInst())
389 return 1
391 def startLoop(self):
392 l = Loop()
393 self.loops.push(l)
394 self.emit('SETUP_LOOP', l.extentAnchor)
395 return l
397 def finishLoop(self):
398 l = self.loops.pop()
399 i = self.code.getCurInst()
400 l.extentAnchor.bind(self.code.getCurInst())
402 def visitFor(self, node):
403 # three refs needed
404 anchor = StackRef()
406 self.emit('SET_LINENO', node.lineno)
407 l = self.startLoop()
408 self.visit(node.list)
409 self.visit(ast.Const(0))
410 l.startAnchor.bind(self.code.getCurInst())
411 self.emit('SET_LINENO', node.lineno)
412 self.emit('FOR_LOOP', anchor)
413 self.visit(node.assign)
414 self.visit(node.body)
415 self.emit('JUMP_ABSOLUTE', l.startAnchor)
416 anchor.bind(self.code.getCurInst())
417 self.emit('POP_BLOCK')
418 if node.else_:
419 self.visit(node.else_)
420 self.finishLoop()
421 return 1
423 def visitWhile(self, node):
424 self.emit('SET_LINENO', node.lineno)
425 l = self.startLoop()
426 if node.else_:
427 lElse = StackRef()
428 else:
429 lElse = l.breakAnchor
430 l.startAnchor.bind(self.code.getCurInst())
431 if hasattr(node.test, 'lineno'):
432 self.emit('SET_LINENO', node.test.lineno)
433 self.visit(node.test)
434 self.emit('JUMP_IF_FALSE', lElse)
435 self.emit('POP_TOP')
436 self.visit(node.body)
437 self.emit('JUMP_ABSOLUTE', l.startAnchor)
438 # note that lElse may be an alias for l.breakAnchor
439 lElse.bind(self.code.getCurInst())
440 self.emit('POP_TOP')
441 self.emit('POP_BLOCK')
442 if node.else_:
443 self.visit(node.else_)
444 self.finishLoop()
445 return 1
447 def visitBreak(self, node):
448 if not self.loops:
449 raise SyntaxError, "'break' outside loop"
450 self.emit('SET_LINENO', node.lineno)
451 self.emit('BREAK_LOOP')
453 def visitContinue(self, node):
454 if not self.loops:
455 raise SyntaxError, "'continue' outside loop"
456 l = self.loops.top()
457 self.emit('SET_LINENO', node.lineno)
458 self.emit('JUMP_ABSOLUTE', l.startAnchor)
460 def visitTryExcept(self, node):
461 # XXX need to figure out exactly what is on the stack when an
462 # exception is raised and the first handler is checked
463 handlers = StackRef()
464 end = StackRef()
465 if node.else_:
466 lElse = StackRef()
467 else:
468 lElse = end
469 self.emit('SET_LINENO', node.lineno)
470 self.emit('SETUP_EXCEPT', handlers)
471 self.visit(node.body)
472 self.emit('POP_BLOCK')
473 self.emit('JUMP_FORWARD', lElse)
474 handlers.bind(self.code.getCurInst())
476 last = len(node.handlers) - 1
477 for i in range(len(node.handlers)):
478 expr, target, body = node.handlers[i]
479 if hasattr(expr, 'lineno'):
480 self.emit('SET_LINENO', expr.lineno)
481 if expr:
482 self.emit('DUP_TOP')
483 self.visit(expr)
484 self.emit('COMPARE_OP', "exception match")
485 next = StackRef()
486 self.emit('JUMP_IF_FALSE', next)
487 self.emit('POP_TOP')
488 self.emit('POP_TOP')
489 if target:
490 self.visit(target)
491 else:
492 self.emit('POP_TOP')
493 self.emit('POP_TOP')
494 self.visit(body)
495 self.emit('JUMP_FORWARD', end)
496 if expr:
497 next.bind(self.code.getCurInst())
498 self.emit('POP_TOP')
499 self.emit('END_FINALLY')
500 if node.else_:
501 lElse.bind(self.code.getCurInst())
502 self.visit(node.else_)
503 end.bind(self.code.getCurInst())
504 return 1
506 def visitTryFinally(self, node):
507 final = StackRef()
508 self.emit('SET_LINENO', node.lineno)
509 self.emit('SETUP_FINALLY', final)
510 self.visit(node.body)
511 self.emit('POP_BLOCK')
512 self.emit('LOAD_CONST', None)
513 final.bind(self.code.getCurInst())
514 self.visit(node.final)
515 self.emit('END_FINALLY')
516 return 1
518 def visitCompare(self, node):
519 """Comment from compile.c follows:
521 The following code is generated for all but the last
522 comparison in a chain:
524 label: on stack: opcode: jump to:
526 a <code to load b>
527 a, b DUP_TOP
528 a, b, b ROT_THREE
529 b, a, b COMPARE_OP
530 b, 0-or-1 JUMP_IF_FALSE L1
531 b, 1 POP_TOP
534 We are now ready to repeat this sequence for the next
535 comparison in the chain.
537 For the last we generate:
539 b <code to load c>
540 b, c COMPARE_OP
541 0-or-1
543 If there were any jumps to L1 (i.e., there was more than one
544 comparison), we generate:
546 0-or-1 JUMP_FORWARD L2
547 L1: b, 0 ROT_TWO
548 0, b POP_TOP
550 L2: 0-or-1
552 self.visit(node.expr)
553 # if refs are never emitted, subsequent bind call has no effect
554 l1 = StackRef()
555 l2 = StackRef()
556 for op, code in node.ops[:-1]:
557 # emit every comparison except the last
558 self.visit(code)
559 self.emit('DUP_TOP')
560 self.emit('ROT_THREE')
561 self.emit('COMPARE_OP', op)
562 # dupTop and compareOp cancel stack effect
563 self.emit('JUMP_IF_FALSE', l1)
564 self.emit('POP_TOP')
565 if node.ops:
566 # emit the last comparison
567 op, code = node.ops[-1]
568 self.visit(code)
569 self.emit('COMPARE_OP', op)
570 if len(node.ops) > 1:
571 self.emit('JUMP_FORWARD', l2)
572 l1.bind(self.code.getCurInst())
573 self.emit('ROT_TWO')
574 self.emit('POP_TOP')
575 l2.bind(self.code.getCurInst())
576 return 1
578 def visitGetattr(self, node):
579 self.visit(node.expr)
580 self.emit('LOAD_ATTR', node.attrname)
581 return 1
583 def visitSubscript(self, node):
584 self.visit(node.expr)
585 for sub in node.subs:
586 self.visit(sub)
587 if len(node.subs) > 1:
588 self.emit('BUILD_TUPLE', len(node.subs))
589 if node.flags == 'OP_APPLY':
590 self.emit('BINARY_SUBSCR')
591 elif node.flags == 'OP_ASSIGN':
592 self.emit('STORE_SUBSCR')
593 elif node.flags == 'OP_DELETE':
594 self.emit('DELETE_SUBSCR')
595 return 1
597 def visitSlice(self, node):
598 self.visit(node.expr)
599 slice = 0
600 if node.lower:
601 self.visit(node.lower)
602 slice = slice | 1
603 if node.upper:
604 self.visit(node.upper)
605 slice = slice | 2
606 if node.flags == 'OP_APPLY':
607 self.emit('SLICE+%d' % slice)
608 elif node.flags == 'OP_ASSIGN':
609 self.emit('STORE_SLICE+%d' % slice)
610 elif node.flags == 'OP_DELETE':
611 self.emit('DELETE_SLICE+%d' % slice)
612 else:
613 print "weird slice", node.flags
614 raise
615 return 1
617 def visitSliceobj(self, node):
618 for child in node.nodes:
619 print child
620 self.visit(child)
621 self.emit('BUILD_SLICE', len(node.nodes))
622 return 1
624 def visitAssign(self, node):
625 self.emit('SET_LINENO', node.lineno)
626 self.visit(node.expr)
627 dups = len(node.nodes) - 1
628 for i in range(len(node.nodes)):
629 elt = node.nodes[i]
630 if i < dups:
631 self.emit('DUP_TOP')
632 if isinstance(elt, ast.Node):
633 self.visit(elt)
634 return 1
636 def visitAssName(self, node):
637 # XXX handle OP_DELETE
638 if node.flags != 'OP_ASSIGN':
639 print "oops", node.flags
640 self.storeName(node.name)
642 def visitAssAttr(self, node):
643 self.visit(node.expr)
644 if node.flags == 'OP_ASSIGN':
645 self.emit('STORE_ATTR', node.attrname)
646 elif node.flags == 'OP_DELETE':
647 self.emit('DELETE_ATTR', node.attrname)
648 else:
649 print "warning: unexpected flags:", node.flags
650 print node
651 return 1
653 def visitAssTuple(self, node):
654 self.emit('UNPACK_TUPLE', len(node.nodes))
655 for child in node.nodes:
656 self.visit(child)
657 return 1
659 visitAssList = visitAssTuple
661 def binaryOp(self, node, op):
662 self.visit(node.left)
663 self.visit(node.right)
664 self.emit(op)
665 return 1
667 def unaryOp(self, node, op):
668 self.visit(node.expr)
669 self.emit(op)
670 return 1
672 def visitAdd(self, node):
673 return self.binaryOp(node, 'BINARY_ADD')
675 def visitSub(self, node):
676 return self.binaryOp(node, 'BINARY_SUBTRACT')
678 def visitMul(self, node):
679 return self.binaryOp(node, 'BINARY_MULTIPLY')
681 def visitDiv(self, node):
682 return self.binaryOp(node, 'BINARY_DIVIDE')
684 def visitMod(self, node):
685 return self.binaryOp(node, 'BINARY_MODULO')
687 def visitPower(self, node):
688 return self.binaryOp(node, 'BINARY_POWER')
690 def visitLeftShift(self, node):
691 return self.binaryOp(node, 'BINARY_LSHIFT')
693 def visitRightShift(self, node):
694 return self.binaryOp(node, 'BINARY_RSHIFT')
696 def visitInvert(self, node):
697 return self.unaryOp(node, 'UNARY_INVERT')
699 def visitUnarySub(self, node):
700 return self.unaryOp(node, 'UNARY_NEGATIVE')
702 def visitUnaryAdd(self, node):
703 return self.unaryOp(node, 'UNARY_POSITIVE')
705 def visitUnaryInvert(self, node):
706 return self.unaryOp(node, 'UNARY_INVERT')
708 def visitNot(self, node):
709 return self.unaryOp(node, 'UNARY_NOT')
711 def visitBackquote(self, node):
712 return self.unaryOp(node, 'UNARY_CONVERT')
714 def bitOp(self, nodes, op):
715 self.visit(nodes[0])
716 for node in nodes[1:]:
717 self.visit(node)
718 self.emit(op)
719 return 1
721 def visitBitand(self, node):
722 return self.bitOp(node.nodes, 'BINARY_AND')
724 def visitBitor(self, node):
725 return self.bitOp(node.nodes, 'BINARY_OR')
727 def visitBitxor(self, node):
728 return self.bitOp(node.nodes, 'BINARY_XOR')
730 def visitTest(self, node, jump):
731 end = StackRef()
732 for child in node.nodes[:-1]:
733 self.visit(child)
734 self.emit(jump, end)
735 self.emit('POP_TOP')
736 self.visit(node.nodes[-1])
737 end.bind(self.code.getCurInst())
738 return 1
740 def visitAssert(self, node):
741 # XXX __debug__ and AssertionError appear to be special cases
742 # -- they are always loaded as globals even if there are local
743 # names. I guess this is a sort of renaming op.
744 skip = StackRef()
745 self.emit('SET_LINENO', node.lineno)
746 self.emit('LOAD_GLOBAL', '__debug__')
747 self.emit('JUMP_IF_FALSE', skip)
748 self.emit('POP_TOP')
749 self.visit(node.test)
750 self.emit('JUMP_IF_TRUE', skip)
751 self.emit('LOAD_GLOBAL', 'AssertionError')
752 self.visit(node.fail)
753 self.emit('RAISE_VARARGS', 2)
754 skip.bind(self.code.getCurInst())
755 self.emit('POP_TOP')
756 return 1
758 def visitAnd(self, node):
759 return self.visitTest(node, 'JUMP_IF_FALSE')
761 def visitOr(self, node):
762 return self.visitTest(node, 'JUMP_IF_TRUE')
764 def visitName(self, node):
765 self.loadName(node.name)
767 def visitConst(self, node):
768 self.emit('LOAD_CONST', node.value)
769 return 1
771 def visitEllipsis(self, node):
772 self.emit('LOAD_CONST', Ellipsis)
773 return 1
775 def visitTuple(self, node):
776 for elt in node.nodes:
777 self.visit(elt)
778 self.emit('BUILD_TUPLE', len(node.nodes))
779 return 1
781 def visitList(self, node):
782 for elt in node.nodes:
783 self.visit(elt)
784 self.emit('BUILD_LIST', len(node.nodes))
785 return 1
787 def visitDict(self, node):
788 self.emit('BUILD_MAP', 0)
789 for k, v in node.items:
790 # XXX need to add set lineno when there aren't constants
791 self.emit('DUP_TOP')
792 self.visit(v)
793 self.emit('ROT_TWO')
794 self.visit(k)
795 self.emit('STORE_SUBSCR')
796 return 1
798 def visitReturn(self, node):
799 self.emit('SET_LINENO', node.lineno)
800 self.visit(node.value)
801 self.emit('RETURN_VALUE')
802 return 1
804 def visitRaise(self, node):
805 self.emit('SET_LINENO', node.lineno)
806 n = 0
807 if node.expr1:
808 self.visit(node.expr1)
809 n = n + 1
810 if node.expr2:
811 self.visit(node.expr2)
812 n = n + 1
813 if node.expr3:
814 self.visit(node.expr3)
815 n = n + 1
816 self.emit('RAISE_VARARGS', n)
817 return 1
819 def visitPrint(self, node):
820 self.emit('SET_LINENO', node.lineno)
821 for child in node.nodes:
822 self.visit(child)
823 self.emit('PRINT_ITEM')
824 return 1
826 def visitPrintnl(self, node):
827 self.visitPrint(node)
828 self.emit('PRINT_NEWLINE')
829 return 1
831 def visitExec(self, node):
832 self.visit(node.expr)
833 if node.locals is None:
834 self.emit('LOAD_CONST', None)
835 else:
836 self.visit(node.locals)
837 if node.globals is None:
838 self.emit('DUP_TOP')
839 else:
840 self.visit(node.globals)
841 self.emit('EXEC_STMT')
843 class LocalNameFinder:
844 def __init__(self, names=()):
845 self.names = misc.Set()
846 self.globals = misc.Set()
847 for name in names:
848 self.names.add(name)
850 def getLocals(self):
851 for elt in self.globals.items():
852 if self.names.has_elt(elt):
853 self.names.remove(elt)
854 return self.names
856 def visitDict(self, node):
857 return 1
859 def visitGlobal(self, node):
860 for name in node.names:
861 self.globals.add(name)
862 return 1
864 def visitFunction(self, node):
865 self.names.add(node.name)
866 return 1
868 def visitLambda(self, node):
869 return 1
871 def visitImport(self, node):
872 for name in node.names:
873 self.names.add(name)
875 def visitFrom(self, node):
876 for name in node.names:
877 self.names.add(name)
879 def visitClassdef(self, node):
880 self.names.add(node.name)
881 return 1
883 def visitAssName(self, node):
884 self.names.add(node.name)
886 class Loop:
887 def __init__(self):
888 self.startAnchor = StackRef()
889 self.breakAnchor = StackRef()
890 self.extentAnchor = StackRef()
892 class CompiledModule:
893 """Store the code object for a compiled module
895 XXX Not clear how the code objects will be stored. Seems possible
896 that a single code attribute is sufficient, because it will
897 contains references to all the need code objects. That might be
898 messy, though.
900 MAGIC = (20121 | (ord('\r')<<16) | (ord('\n')<<24))
902 def __init__(self, source, filename):
903 self.source = source
904 self.filename = filename
906 def compile(self):
907 t = transformer.Transformer()
908 self.ast = t.parsesuite(self.source)
909 cg = CodeGenerator(self.filename)
910 walk(self.ast, cg)
911 self.code = cg.asConst()
913 def dump(self, path):
914 """create a .pyc file"""
915 f = open(path, 'wb')
916 f.write(self._pyc_header())
917 marshal.dump(self.code, f)
918 f.close()
920 def _pyc_header(self):
921 # compile.c uses marshal to write a long directly, with
922 # calling the interface that would also generate a 1-byte code
923 # to indicate the type of the value. simplest way to get the
924 # same effect is to call marshal and then skip the code.
925 magic = marshal.dumps(self.MAGIC)[1:]
926 mtime = os.stat(self.filename)[stat.ST_MTIME]
927 mtime = struct.pack('i', mtime)
928 return magic + mtime
930 if __name__ == "__main__":
931 import getopt
933 VERBOSE = 0
934 opts, args = getopt.getopt(sys.argv[1:], 'vq')
935 for k, v in opts:
936 if k == '-v':
937 VERBOSE = 1
938 ASTVisitor.VERBOSE = ASTVisitor.VERBOSE + 1
939 if k == '-q':
940 f = open('/dev/null', 'wb')
941 sys.stdout = f
942 if not args:
943 print "no files to compile"
944 else:
945 for filename in args:
946 if VERBOSE:
947 print filename
948 buf = open(filename).read()
949 mod = CompiledModule(buf, filename)
950 mod.compile()
951 mod.dump(filename + 'c')