[Python-checkins] CVS: python/dist/src/Tools/compiler/compiler pyassem.py,1.9,1.10 pycodegen.py,1.21,1.22 visitor.py,1.3,1.4

Thomas Wouters python-dev@python.org
Sat, 12 Aug 2000 13:32:49 -0700


Update of /cvsroot/python/python/dist/src/Tools/compiler/compiler
In directory slayer.i.sourceforge.net:/tmp/cvs-serv6567/compiler

Modified Files:
	pyassem.py pycodegen.py visitor.py 
Log Message:

Bring Tools/compiler almost up to date. Specifically:

- fix tab space issues (SF patch #101167 by Neil Schemenauer)
- fix co_flags for classes to include CO_NEWLOCALS (SF patch #101145 by Neil)
- fix for merger of UNPACK_LIST and UNPACK_TUPLE into UNPACK_SEQUENCE,
  (SF patch #101168 by, well, Neil :)
- Adjust bytecode MAGIC to current bytecode.

TODO: teach compile.py about list comprehensions.



Index: pyassem.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Tools/compiler/compiler/pyassem.py,v
retrieving revision 1.9
retrieving revision 1.10
diff -C2 -r1.9 -r1.10
*** pyassem.py	2000/05/02 22:32:59	1.9
--- pyassem.py	2000/08/12 20:32:46	1.10
***************
*** 10,69 ****
  class FlowGraph:
      def __init__(self):
! 	self.current = self.entry = Block()
! 	self.exit = Block("exit")
! 	self.blocks = misc.Set()
! 	self.blocks.add(self.entry)
! 	self.blocks.add(self.exit)
  
      def startBlock(self, block):
! 	self.current = block
  
      def nextBlock(self, block=None):
! 	if block is None:
! 	    block = self.newBlock()
! 	# XXX think we need to specify when there is implicit transfer
! 	# from one block to the next
! 	#
! 	# I think this strategy works: each block has a child
! 	# designated as "next" which is returned as the last of the
! 	# children.  because the nodes in a graph are emitted in
! 	# reverse post order, the "next" block will always be emitted
! 	# immediately after its parent.
! 	# Worry: maintaining this invariant could be tricky
! 	self.current.addNext(block)
! 	self.startBlock(block)
  
      def newBlock(self):
! 	b = Block()
! 	self.blocks.add(b)
! 	return b
  
      def startExitBlock(self):
! 	self.startBlock(self.exit)
  
      def emit(self, *inst):
! 	# XXX should jump instructions implicitly call nextBlock?
! 	if inst[0] == 'RETURN_VALUE':
! 	    self.current.addOutEdge(self.exit)
! 	self.current.emit(inst)
  
      def getBlocks(self):
! 	"""Return the blocks in reverse postorder
  
! 	i.e. each node appears before all of its successors
! 	"""
! 	# XXX make sure every node that doesn't have an explicit next
! 	# is set so that next points to exit
! 	for b in self.blocks.elements():
! 	    if b is self.exit:
! 		continue
! 	    if not b.next:
! 		b.addNext(self.exit)
! 	order = dfs_postorder(self.entry, {})
! 	order.reverse()
! 	# hack alert
! 	if not self.exit in order:
! 	    order.append(self.exit)
! 	return order
  
  def dfs_postorder(b, seen):
--- 10,69 ----
  class FlowGraph:
      def __init__(self):
!         self.current = self.entry = Block()
!         self.exit = Block("exit")
!         self.blocks = misc.Set()
!         self.blocks.add(self.entry)
!         self.blocks.add(self.exit)
  
      def startBlock(self, block):
!         self.current = block
  
      def nextBlock(self, block=None):
!         if block is None:
!             block = self.newBlock()
!         # XXX think we need to specify when there is implicit transfer
!         # from one block to the next
!         #
!         # I think this strategy works: each block has a child
!         # designated as "next" which is returned as the last of the
!         # children.  because the nodes in a graph are emitted in
!         # reverse post order, the "next" block will always be emitted
!         # immediately after its parent.
!         # Worry: maintaining this invariant could be tricky
!         self.current.addNext(block)
!         self.startBlock(block)
  
      def newBlock(self):
!         b = Block()
!         self.blocks.add(b)
!         return b
  
      def startExitBlock(self):
!         self.startBlock(self.exit)
  
      def emit(self, *inst):
!         # XXX should jump instructions implicitly call nextBlock?
!         if inst[0] == 'RETURN_VALUE':
!             self.current.addOutEdge(self.exit)
!         self.current.emit(inst)
  
      def getBlocks(self):
!         """Return the blocks in reverse postorder
  
!         i.e. each node appears before all of its successors
!         """
!         # XXX make sure every node that doesn't have an explicit next
!         # is set so that next points to exit
!         for b in self.blocks.elements():
!             if b is self.exit:
!                 continue
!             if not b.next:
!                 b.addNext(self.exit)
!         order = dfs_postorder(self.entry, {})
!         order.reverse()
!         # hack alert
!         if not self.exit in order:
!             order.append(self.exit)
!         return order
  
  def dfs_postorder(b, seen):
***************
*** 72,78 ****
      seen[b] = b
      for c in b.children():
! 	if seen.has_key(c):
! 	    continue
! 	order = order + dfs_postorder(c, seen)
      order.append(b)
      return order
--- 72,78 ----
      seen[b] = b
      for c in b.children():
!         if seen.has_key(c):
!             continue
!         order = order + dfs_postorder(c, seen)
      order.append(b)
      return order
***************
*** 82,126 ****
  
      def __init__(self, label=''):
! 	self.insts = []
! 	self.inEdges = misc.Set()
! 	self.outEdges = misc.Set()
! 	self.label = label
! 	self.bid = Block._count
! 	self.next = []
! 	Block._count = Block._count + 1
  
      def __repr__(self):
! 	if self.label:
! 	    return "<block %s id=%d len=%d>" % (self.label, self.bid,
! 						len(self.insts)) 
! 	else:
! 	    return "<block id=%d len=%d>" % (self.bid, len(self.insts))
  
      def __str__(self):
! 	insts = map(str, self.insts)
! 	return "<block %s %d:\n%s>" % (self.label, self.bid,
! 				       string.join(insts, '\n')) 
  
      def emit(self, inst):
! 	op = inst[0]
! 	if op[:4] == 'JUMP':
! 	    self.outEdges.add(inst[1])
! 	self.insts.append(inst)
  
      def getInstructions(self):
! 	return self.insts
  
      def addInEdge(self, block):
! 	self.inEdges.add(block)
  
      def addOutEdge(self, block):
! 	self.outEdges.add(block)
  
      def addNext(self, block):
! 	self.next.append(block)
! 	assert len(self.next) == 1, map(str, self.next)
  
      def children(self):
! 	return self.outEdges.elements() + self.next
  
  # flags for code objects
--- 82,126 ----
  
      def __init__(self, label=''):
!         self.insts = []
!         self.inEdges = misc.Set()
!         self.outEdges = misc.Set()
!         self.label = label
!         self.bid = Block._count
!         self.next = []
!         Block._count = Block._count + 1
  
      def __repr__(self):
!         if self.label:
!             return "<block %s id=%d len=%d>" % (self.label, self.bid,
!                                                 len(self.insts)) 
!         else:
!             return "<block id=%d len=%d>" % (self.bid, len(self.insts))
  
      def __str__(self):
!         insts = map(str, self.insts)
!         return "<block %s %d:\n%s>" % (self.label, self.bid,
!                                        string.join(insts, '\n')) 
  
      def emit(self, inst):
!         op = inst[0]
!         if op[:4] == 'JUMP':
!             self.outEdges.add(inst[1])
!         self.insts.append(inst)
  
      def getInstructions(self):
!         return self.insts
  
      def addInEdge(self, block):
!         self.inEdges.add(block)
  
      def addOutEdge(self, block):
!         self.outEdges.add(block)
  
      def addNext(self, block):
!         self.next.append(block)
!         assert len(self.next) == 1, map(str, self.next)
  
      def children(self):
!         return self.outEdges.elements() + self.next
  
  # flags for code objects
***************
*** 140,155 ****
  
      def __init__(self, name, filename, args=(), optimized=0):
! 	self.super_init()
! 	self.name = name
! 	self.filename = filename
! 	self.docstring = None
! 	self.args = args # XXX
! 	self.argcount = getArgCount(args)
! 	if optimized:
! 	    self.flags = CO_OPTIMIZED | CO_NEWLOCALS 
! 	else:
! 	    self.flags = 0
! 	self.consts = []
! 	self.names = []
          self.varnames = list(args) or []
          for i in range(len(self.varnames)):
--- 140,155 ----
  
      def __init__(self, name, filename, args=(), optimized=0):
!         self.super_init()
!         self.name = name
!         self.filename = filename
!         self.docstring = None
!         self.args = args # XXX
!         self.argcount = getArgCount(args)
!         if optimized:
!             self.flags = CO_OPTIMIZED | CO_NEWLOCALS 
!         else:
!             self.flags = 0
!         self.consts = []
!         self.names = []
          self.varnames = list(args) or []
          for i in range(len(self.varnames)):
***************
*** 164,174 ****
  
      def setFlag(self, flag):
! 	self.flags = self.flags | flag
! 	if flag == CO_VARARGS:
! 	    self.argcount = self.argcount - 1
  
      def getCode(self):
! 	"""Get a Python code object"""
! 	if self.stage == RAW:
              self.flattenGraph()
          if self.stage == FLAT:
--- 164,174 ----
  
      def setFlag(self, flag):
!         self.flags = self.flags | flag
!         if flag == CO_VARARGS:
!             self.argcount = self.argcount - 1
  
      def getCode(self):
!         """Get a Python code object"""
!         if self.stage == RAW:
              self.flattenGraph()
          if self.stage == FLAT:
***************
*** 199,227 ****
  
      def flattenGraph(self):
! 	"""Arrange the blocks in order and resolve jumps"""
! 	assert self.stage == RAW
! 	self.insts = insts = []
! 	pc = 0
! 	begin = {}
! 	end = {}
! 	for b in self.getBlocks():
! 	    begin[b] = pc
! 	    for inst in b.getInstructions():
! 		insts.append(inst)
! 		if len(inst) == 1:
! 		    pc = pc + 1
! 		else:
! 		    # arg takes 2 bytes
! 		    pc = pc + 3
! 	    end[b] = pc
! 	pc = 0
! 	for i in range(len(insts)):
! 	    inst = insts[i]
! 	    if len(inst) == 1:
                  pc = pc + 1
              else:
                  pc = pc + 3
! 	    opname = inst[0]
! 	    if self.hasjrel.has_elt(opname):
                  oparg = inst[1]
                  offset = begin[oparg] - pc
--- 199,227 ----
  
      def flattenGraph(self):
!         """Arrange the blocks in order and resolve jumps"""
!         assert self.stage == RAW
!         self.insts = insts = []
!         pc = 0
!         begin = {}
!         end = {}
!         for b in self.getBlocks():
!             begin[b] = pc
!             for inst in b.getInstructions():
!                 insts.append(inst)
!                 if len(inst) == 1:
!                     pc = pc + 1
!                 else:
!                     # arg takes 2 bytes
!                     pc = pc + 3
!             end[b] = pc
!         pc = 0
!         for i in range(len(insts)):
!             inst = insts[i]
!             if len(inst) == 1:
                  pc = pc + 1
              else:
                  pc = pc + 3
!             opname = inst[0]
!             if self.hasjrel.has_elt(opname):
                  oparg = inst[1]
                  offset = begin[oparg] - pc
***************
*** 229,234 ****
              elif self.hasjabs.has_elt(opname):
                  insts[i] = opname, begin[inst[1]]
! 	self.stacksize = findDepth(self.insts)
! 	self.stage = FLAT
  
      hasjrel = misc.Set()
--- 229,234 ----
              elif self.hasjabs.has_elt(opname):
                  insts[i] = opname, begin[inst[1]]
!         self.stacksize = findDepth(self.insts)
!         self.stage = FLAT
  
      hasjrel = misc.Set()
***************
*** 293,297 ****
      _cmp = list(dis.cmp_op)
      def _convert_COMPARE_OP(self, arg):
! 	return self._cmp.index(arg)
  
      # similarly for other opcodes...
--- 293,297 ----
      _cmp = list(dis.cmp_op)
      def _convert_COMPARE_OP(self, arg):
!         return self._cmp.index(arg)
  
      # similarly for other opcodes...
***************
*** 315,324 ****
                      lnotab.nextLine(oparg)
                  hi, lo = twobyte(oparg)
! 		try:
! 		    lnotab.addCode(self.opnum[opname], lo, hi)
! 		except ValueError:
! 		    print opname, oparg
! 		    print self.opnum[opname], lo, hi
! 		    raise
          self.stage = DONE
  
--- 315,324 ----
                      lnotab.nextLine(oparg)
                  hi, lo = twobyte(oparg)
!                 try:
!                     lnotab.addCode(self.opnum[opname], lo, hi)
!                 except ValueError:
!                     print opname, oparg
!                     print self.opnum[opname], lo, hi
!                     raise
          self.stage = DONE
  
***************
*** 355,362 ****
              l.append(elt)
          return tuple(l)
! 	    
  def isJump(opname):
      if opname[:4] == 'JUMP':
! 	return 1
  
  class TupleArg:
--- 355,362 ----
              l.append(elt)
          return tuple(l)
!             
  def isJump(opname):
      if opname[:4] == 'JUMP':
!         return 1
  
  class TupleArg:
***************
*** 373,380 ****
      argcount = len(args)
      if args:
! 	for arg in args:
! 	    if isinstance(arg, TupleArg):
! 		numNames = len(misc.flatten(arg.names))
! 		argcount = argcount - numNames
      return argcount
  
--- 373,380 ----
      argcount = len(args)
      if args:
!         for arg in args:
!             if isinstance(arg, TupleArg):
!                 numNames = len(misc.flatten(arg.names))
!                 argcount = argcount - numNames
      return argcount
  
***************
*** 514,522 ****
      
      # special cases:
!     # UNPACK_TUPLE, UNPACK_LIST, BUILD_TUPLE,
      # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
!     def UNPACK_TUPLE(self, count):
!         return count
!     def UNPACK_LIST(self, count):
          return count
      def BUILD_TUPLE(self, count):
--- 514,520 ----
      
      # special cases:
!     # UNPACK_SEQUENCE, BUILD_TUPLE,
      # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
!     def UNPACK_SEQUENCE(self, count):
          return count
      def BUILD_TUPLE(self, count):

Index: pycodegen.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Tools/compiler/compiler/pycodegen.py,v
retrieving revision 1.21
retrieving revision 1.22
diff -C2 -r1.21 -r1.22
*** pycodegen.py	2000/08/04 16:56:51	1.21
--- pycodegen.py	2000/08/12 20:32:46	1.22
***************
*** 8,12 ****
  from compiler import ast, parse, walk
  from compiler import pyassem, misc
! from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, TupleArg
  
  callfunc_opcode_info = {
--- 8,12 ----
  from compiler import ast, parse, walk
  from compiler import pyassem, misc
! from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS, TupleArg
  
  callfunc_opcode_info = {
***************
*** 30,59 ****
  class Module:
      def __init__(self, source, filename):
! 	self.filename = filename
! 	self.source = source
! 	self.code = None
  
      def compile(self):
! 	ast = parse(self.source)
          root, filename = os.path.split(self.filename)
! 	gen = ModuleCodeGenerator(filename)
! 	walk(ast, gen, 1)
! 	self.code = gen.getCode()
  
      def dump(self, f):
! 	f.write(self.getPycHeader())
! 	marshal.dump(self.code, f)
  
!     MAGIC = (20121 | (ord('\r')<<16) | (ord('\n')<<24))
  
      def getPycHeader(self):
! 	# compile.c uses marshal to write a long directly, with
! 	# calling the interface that would also generate a 1-byte code
! 	# to indicate the type of the value.  simplest way to get the
! 	# same effect is to call marshal and then skip the code.
! 	magic = marshal.dumps(self.MAGIC)[1:]
! 	mtime = os.stat(self.filename)[stat.ST_MTIME]
! 	mtime = struct.pack('i', mtime)
! 	return magic + mtime
  
  class CodeGenerator:
--- 30,59 ----
  class Module:
      def __init__(self, source, filename):
!         self.filename = filename
!         self.source = source
!         self.code = None
  
      def compile(self):
!         ast = parse(self.source)
          root, filename = os.path.split(self.filename)
!         gen = ModuleCodeGenerator(filename)
!         walk(ast, gen, 1)
!         self.code = gen.getCode()
  
      def dump(self, f):
!         f.write(self.getPycHeader())
!         marshal.dump(self.code, f)
  
!     MAGIC = (50811 | (ord('\r')<<16) | (ord('\n')<<24))
  
      def getPycHeader(self):
!         # compile.c uses marshal to write a long directly, with
!         # calling the interface that would also generate a 1-byte code
!         # to indicate the type of the value.  simplest way to get the
!         # same effect is to call marshal and then skip the code.
!         magic = marshal.dumps(self.MAGIC)[1:]
!         mtime = os.stat(self.filename)[stat.ST_MTIME]
!         mtime = struct.pack('i', mtime)
!         return magic + mtime
  
  class CodeGenerator:
***************
*** 64,85 ****
  ## Subclasses must define a constructor that intializes self.graph
  ## before calling this init function
! ## 	self.graph = pyassem.PyFlowGraph()
! 	self.filename = filename
! 	self.locals = misc.Stack()
! 	self.loops = misc.Stack()
! 	self.curStack = 0
! 	self.maxStack = 0
! 	self._setupGraphDelegation()
  
      def _setupGraphDelegation(self):
! 	self.emit = self.graph.emit
! 	self.newBlock = self.graph.newBlock
! 	self.startBlock = self.graph.startBlock
! 	self.nextBlock = self.graph.nextBlock
! 	self.setDocstring = self.graph.setDocstring
  
      def getCode(self):
! 	"""Return a code object"""
! 	return self.graph.getCode()
  
      # Next five methods handle name access
--- 64,85 ----
  ## Subclasses must define a constructor that intializes self.graph
  ## before calling this init function
! ##         self.graph = pyassem.PyFlowGraph()
!         self.filename = filename
!         self.locals = misc.Stack()
!         self.loops = misc.Stack()
!         self.curStack = 0
!         self.maxStack = 0
!         self._setupGraphDelegation()
  
      def _setupGraphDelegation(self):
!         self.emit = self.graph.emit
!         self.newBlock = self.graph.newBlock
!         self.startBlock = self.graph.startBlock
!         self.nextBlock = self.graph.nextBlock
!         self.setDocstring = self.graph.setDocstring
  
      def getCode(self):
!         """Return a code object"""
!         return self.graph.getCode()
  
      # Next five methods handle name access
***************
*** 98,106 ****
  
      def _nameOp(self, prefix, name):
! 	if not self.optimized:
! 	    self.emit(prefix + '_NAME', name)
! 	    return
          if self.isLocalName(name):
! 	    self.emit(prefix + '_FAST', name)
          else:
              self.emit(prefix + '_GLOBAL', name)
--- 98,106 ----
  
      def _nameOp(self, prefix, name):
!         if not self.optimized:
!             self.emit(prefix + '_NAME', name)
!             return
          if self.isLocalName(name):
!             self.emit(prefix + '_FAST', name)
          else:
              self.emit(prefix + '_GLOBAL', name)
***************
*** 126,148 ****
  
      def visitModule(self, node):
! 	lnf = walk(node.node, LocalNameFinder(), 0)
! 	self.locals.push(lnf.getLocals())
! 	self.setDocstring(node.doc)
! 	self.visit(node.node)
! 	self.emit('LOAD_CONST', None)
! 	self.emit('RETURN_VALUE')
  
      def visitFunction(self, node):
! 	self._visitFuncOrLambda(node, isLambda=0)
! 	self.storeName(node.name)
  
      def visitLambda(self, node):
! 	self._visitFuncOrLambda(node, isLambda=1)
! ##	self.storeName("<lambda>")
  
      def _visitFuncOrLambda(self, node, isLambda):
! 	gen = FunctionCodeGenerator(node, self.filename, isLambda)
! 	walk(node.code, gen)
! 	gen.finish()
          self.set_lineno(node)
          for default in node.defaults:
--- 126,148 ----
  
      def visitModule(self, node):
!         lnf = walk(node.node, LocalNameFinder(), 0)
!         self.locals.push(lnf.getLocals())
!         self.setDocstring(node.doc)
!         self.visit(node.node)
!         self.emit('LOAD_CONST', None)
!         self.emit('RETURN_VALUE')
  
      def visitFunction(self, node):
!         self._visitFuncOrLambda(node, isLambda=0)
!         self.storeName(node.name)
  
      def visitLambda(self, node):
!         self._visitFuncOrLambda(node, isLambda=1)
! ##        self.storeName("<lambda>")
  
      def _visitFuncOrLambda(self, node, isLambda):
!         gen = FunctionCodeGenerator(node, self.filename, isLambda)
!         walk(node.code, gen)
!         gen.finish()
          self.set_lineno(node)
          for default in node.defaults:
***************
*** 171,236 ****
  
      def visitIf(self, node):
! 	end = self.newBlock()
! 	numtests = len(node.tests)
! 	for i in range(numtests):
! 	    test, suite = node.tests[i]
              self.set_lineno(test)
! 	    self.visit(test)
! ## 	    if i == numtests - 1 and not node.else_:
! ## 		nextTest = end
! ## 	    else:
! ## 		nextTest = self.newBlock()
! 	    nextTest = self.newBlock()
! 	    self.emit('JUMP_IF_FALSE', nextTest)
! 	    self.nextBlock()
! 	    self.emit('POP_TOP')
! 	    self.visit(suite)
! 	    self.emit('JUMP_FORWARD', end)
! 	    self.nextBlock(nextTest)
! 	    self.emit('POP_TOP')
! 	if node.else_:
! 	    self.visit(node.else_)
! 	self.nextBlock(end)
  
      def visitWhile(self, node):
          self.set_lineno(node)
  
! 	loop = self.newBlock()
! 	else_ = self.newBlock()
  
! 	after = self.newBlock()
! 	self.emit('SETUP_LOOP', after)
  
! 	self.nextBlock(loop)
! 	self.loops.push(loop)
  
          self.set_lineno(node)
! 	self.visit(node.test)
! 	self.emit('JUMP_IF_FALSE', else_ or after)
! 
! 	self.nextBlock()
! 	self.emit('POP_TOP')
! 	self.visit(node.body)
! 	self.emit('JUMP_ABSOLUTE', loop)
! 
! 	self.startBlock(else_) # or just the POPs if not else clause
! 	self.emit('POP_TOP')
! 	self.emit('POP_BLOCK')
! 	if node.else_:
! 	    self.visit(node.else_)
! 	self.loops.pop()
! 	self.nextBlock(after)
  
      def visitFor(self, node):
! 	start = self.newBlock()
          anchor = self.newBlock()
! 	after = self.newBlock()
          self.loops.push(start)
  
          self.set_lineno(node)
! 	self.emit('SETUP_LOOP', after)
          self.visit(node.list)
          self.visit(ast.Const(0))
! 	self.nextBlock(start)
          self.set_lineno(node)
          self.emit('FOR_LOOP', anchor)
--- 171,236 ----
  
      def visitIf(self, node):
!         end = self.newBlock()
!         numtests = len(node.tests)
!         for i in range(numtests):
!             test, suite = node.tests[i]
              self.set_lineno(test)
!             self.visit(test)
! ##            if i == numtests - 1 and not node.else_:
! ##                nextTest = end
! ##            else:
! ##                nextTest = self.newBlock()
!             nextTest = self.newBlock()
!             self.emit('JUMP_IF_FALSE', nextTest)
!             self.nextBlock()
!             self.emit('POP_TOP')
!             self.visit(suite)
!             self.emit('JUMP_FORWARD', end)
!             self.nextBlock(nextTest)
!             self.emit('POP_TOP')
!         if node.else_:
!             self.visit(node.else_)
!         self.nextBlock(end)
  
      def visitWhile(self, node):
          self.set_lineno(node)
  
!         loop = self.newBlock()
!         else_ = self.newBlock()
  
!         after = self.newBlock()
!         self.emit('SETUP_LOOP', after)
  
!         self.nextBlock(loop)
!         self.loops.push(loop)
  
          self.set_lineno(node)
!         self.visit(node.test)
!         self.emit('JUMP_IF_FALSE', else_ or after)
! 
!         self.nextBlock()
!         self.emit('POP_TOP')
!         self.visit(node.body)
!         self.emit('JUMP_ABSOLUTE', loop)
! 
!         self.startBlock(else_) # or just the POPs if not else clause
!         self.emit('POP_TOP')
!         self.emit('POP_BLOCK')
!         if node.else_:
!             self.visit(node.else_)
!         self.loops.pop()
!         self.nextBlock(after)
  
      def visitFor(self, node):
!         start = self.newBlock()
          anchor = self.newBlock()
!         after = self.newBlock()
          self.loops.push(start)
  
          self.set_lineno(node)
!         self.emit('SETUP_LOOP', after)
          self.visit(node.list)
          self.visit(ast.Const(0))
!         self.nextBlock(start)
          self.set_lineno(node)
          self.emit('FOR_LOOP', anchor)
***************
*** 238,326 ****
          self.visit(node.body)
          self.emit('JUMP_ABSOLUTE', start)
! 	self.nextBlock(anchor)
          self.emit('POP_BLOCK')
          if node.else_:
              self.visit(node.else_)
! 	self.loops.pop()
! 	self.nextBlock(after)
  
      def visitBreak(self, node):
! 	if not self.loops:
! 	    raise SyntaxError, "'break' outside loop (%s, %d)" % \
! 		  (self.filename, node.lineno)
          self.set_lineno(node)
! 	self.emit('BREAK_LOOP')
  
      def visitContinue(self, node):
          if not self.loops:
              raise SyntaxError, "'continue' outside loop (%s, %d)" % \
! 		  (self.filename, node.lineno)
          l = self.loops.top()
          self.set_lineno(node)
          self.emit('JUMP_ABSOLUTE', l)
! 	self.nextBlock()
  
      def visitTest(self, node, jump):
! 	end = self.newBlock()
          for child in node.nodes[:-1]:
              self.visit(child)
              self.emit(jump, end)
! 	    self.nextBlock()
              self.emit('POP_TOP')
          self.visit(node.nodes[-1])
! 	self.nextBlock(end)
  
      def visitAnd(self, node):
! 	self.visitTest(node, 'JUMP_IF_FALSE')
  
      def visitOr(self, node):
! 	self.visitTest(node, 'JUMP_IF_TRUE')
  
      def visitCompare(self, node):
! 	self.visit(node.expr)
! 	cleanup = self.newBlock()
! 	for op, code in node.ops[:-1]:
! 	    self.visit(code)
! 	    self.emit('DUP_TOP')
! 	    self.emit('ROT_THREE')
! 	    self.emit('COMPARE_OP', op)
! 	    self.emit('JUMP_IF_FALSE', cleanup)
! 	    self.nextBlock()
! 	    self.emit('POP_TOP')
! 	# now do the last comparison
! 	if node.ops:
! 	    op, code = node.ops[-1]
! 	    self.visit(code)
! 	    self.emit('COMPARE_OP', op)
! 	if len(node.ops) > 1:
! 	    end = self.newBlock()
! 	    self.emit('JUMP_FORWARD', end)
! 	    self.nextBlock(cleanup)
! 	    self.emit('ROT_TWO')
! 	    self.emit('POP_TOP')
! 	    self.nextBlock(end)
  
      # exception related
  
      def visitAssert(self, node):
! 	# XXX would be interesting to implement this via a
! 	# transformation of the AST before this stage
! 	end = self.newBlock()
          self.set_lineno(node)
          # XXX __debug__ and AssertionError appear to be special cases
          # -- they are always loaded as globals even if there are local
          # names.  I guess this is a sort of renaming op.
! 	self.emit('LOAD_GLOBAL', '__debug__')
! 	self.emit('JUMP_IF_FALSE', end)
! 	self.nextBlock()
! 	self.emit('POP_TOP')
! 	self.visit(node.test)
! 	self.emit('JUMP_IF_TRUE', end)
! 	self.nextBlock()
! 	self.emit('LOAD_GLOBAL', 'AssertionError')
! 	self.visit(node.fail)
! 	self.emit('RAISE_VARARGS', 2)
! 	self.nextBlock(end)
! 	self.emit('POP_TOP')
  
      def visitRaise(self, node):
--- 238,326 ----
          self.visit(node.body)
          self.emit('JUMP_ABSOLUTE', start)
!         self.nextBlock(anchor)
          self.emit('POP_BLOCK')
          if node.else_:
              self.visit(node.else_)
!         self.loops.pop()
!         self.nextBlock(after)
  
      def visitBreak(self, node):
!         if not self.loops:
!             raise SyntaxError, "'break' outside loop (%s, %d)" % \
!                   (self.filename, node.lineno)
          self.set_lineno(node)
!         self.emit('BREAK_LOOP')
  
      def visitContinue(self, node):
          if not self.loops:
              raise SyntaxError, "'continue' outside loop (%s, %d)" % \
!                   (self.filename, node.lineno)
          l = self.loops.top()
          self.set_lineno(node)
          self.emit('JUMP_ABSOLUTE', l)
!         self.nextBlock()
  
      def visitTest(self, node, jump):
!         end = self.newBlock()
          for child in node.nodes[:-1]:
              self.visit(child)
              self.emit(jump, end)
!             self.nextBlock()
              self.emit('POP_TOP')
          self.visit(node.nodes[-1])
!         self.nextBlock(end)
  
      def visitAnd(self, node):
!         self.visitTest(node, 'JUMP_IF_FALSE')
  
      def visitOr(self, node):
!         self.visitTest(node, 'JUMP_IF_TRUE')
  
      def visitCompare(self, node):
!         self.visit(node.expr)
!         cleanup = self.newBlock()
!         for op, code in node.ops[:-1]:
!             self.visit(code)
!             self.emit('DUP_TOP')
!             self.emit('ROT_THREE')
!             self.emit('COMPARE_OP', op)
!             self.emit('JUMP_IF_FALSE', cleanup)
!             self.nextBlock()
!             self.emit('POP_TOP')
!         # now do the last comparison
!         if node.ops:
!             op, code = node.ops[-1]
!             self.visit(code)
!             self.emit('COMPARE_OP', op)
!         if len(node.ops) > 1:
!             end = self.newBlock()
!             self.emit('JUMP_FORWARD', end)
!             self.nextBlock(cleanup)
!             self.emit('ROT_TWO')
!             self.emit('POP_TOP')
!             self.nextBlock(end)
  
      # exception related
  
      def visitAssert(self, node):
!         # XXX would be interesting to implement this via a
!         # transformation of the AST before this stage
!         end = self.newBlock()
          self.set_lineno(node)
          # XXX __debug__ and AssertionError appear to be special cases
          # -- they are always loaded as globals even if there are local
          # names.  I guess this is a sort of renaming op.
!         self.emit('LOAD_GLOBAL', '__debug__')
!         self.emit('JUMP_IF_FALSE', end)
!         self.nextBlock()
!         self.emit('POP_TOP')
!         self.visit(node.test)
!         self.emit('JUMP_IF_TRUE', end)
!         self.nextBlock()
!         self.emit('LOAD_GLOBAL', 'AssertionError')
!         self.visit(node.fail)
!         self.emit('RAISE_VARARGS', 2)
!         self.nextBlock(end)
!         self.emit('POP_TOP')
  
      def visitRaise(self, node):
***************
*** 350,354 ****
          self.emit('POP_BLOCK')
          self.emit('JUMP_FORWARD', lElse)
! 	self.nextBlock(handlers)
          
          last = len(node.handlers) - 1
--- 350,354 ----
          self.emit('POP_BLOCK')
          self.emit('JUMP_FORWARD', lElse)
!         self.nextBlock(handlers)
          
          last = len(node.handlers) - 1
***************
*** 362,366 ****
                  next = self.newBlock()
                  self.emit('JUMP_IF_FALSE', next)
! 		self.nextBlock()
                  self.emit('POP_TOP')
              self.emit('POP_TOP')
--- 362,366 ----
                  next = self.newBlock()
                  self.emit('JUMP_IF_FALSE', next)
!                 self.nextBlock()
                  self.emit('POP_TOP')
              self.emit('POP_TOP')
***************
*** 373,383 ****
              self.emit('JUMP_FORWARD', end)
              if expr:
! 		self.nextBlock(next)
              self.emit('POP_TOP')
          self.emit('END_FINALLY')
          if node.else_:
! 	    self.nextBlock(lElse)
              self.visit(node.else_)
! 	self.nextBlock(end)
      
      def visitTryFinally(self, node):
--- 373,383 ----
              self.emit('JUMP_FORWARD', end)
              if expr:
!                 self.nextBlock(next)
              self.emit('POP_TOP')
          self.emit('END_FINALLY')
          if node.else_:
!             self.nextBlock(lElse)
              self.visit(node.else_)
!         self.nextBlock(end)
      
      def visitTryFinally(self, node):
***************
*** 388,392 ****
          self.emit('POP_BLOCK')
          self.emit('LOAD_CONST', None)
! 	self.nextBlock(final)
          self.visit(node.final)
          self.emit('END_FINALLY')
--- 388,392 ----
          self.emit('POP_BLOCK')
          self.emit('LOAD_CONST', None)
!         self.nextBlock(final)
          self.visit(node.final)
          self.emit('END_FINALLY')
***************
*** 395,400 ****
  
  ##     def visitStmt(self, node):
! ## 	# nothing to do except walk the children
! ## 	pass
  
      def visitDiscard(self, node):
--- 395,400 ----
  
  ##     def visitStmt(self, node):
! ##         # nothing to do except walk the children
! ##         pass
  
      def visitDiscard(self, node):
***************
*** 406,415 ****
  
      def visitKeyword(self, node):
! 	self.emit('LOAD_CONST', node.name)
! 	self.visit(node.expr)
  
      def visitGlobal(self, node):
          # no code to generate
! 	pass
  
      def visitName(self, node):
--- 406,415 ----
  
      def visitKeyword(self, node):
!         self.emit('LOAD_CONST', node.name)
!         self.visit(node.expr)
  
      def visitGlobal(self, node):
          # no code to generate
!         pass
  
      def visitName(self, node):
***************
*** 471,475 ****
      def visitAssTuple(self, node):
          if findOp(node) != 'OP_DELETE':
!             self.emit('UNPACK_TUPLE', len(node.nodes))
          for child in node.nodes:
              self.visit(child)
--- 471,475 ----
      def visitAssTuple(self, node):
          if findOp(node) != 'OP_DELETE':
!             self.emit('UNPACK_SEQUENCE', len(node.nodes))
          for child in node.nodes:
              self.visit(child)
***************
*** 656,663 ****
          self.emit('BUILD_MAP', 0)
          for k, v in node.items:
! 	    lineno2 = getattr(node, 'lineno', None)
              if lineno2 is not None and lineno != lineno2:
! 		self.emit('SET_LINENO', lineno2)
! 		lineno = lineno2
              self.emit('DUP_TOP')
              self.visit(v)
--- 656,663 ----
          self.emit('BUILD_MAP', 0)
          for k, v in node.items:
!             lineno2 = getattr(node, 'lineno', None)
              if lineno2 is not None and lineno != lineno2:
!                 self.emit('SET_LINENO', lineno2)
!                 lineno = lineno2
              self.emit('DUP_TOP')
              self.visit(v)
***************
*** 670,676 ****
      
      def __init__(self, filename):
! 	# XXX <module> is ? in compile.c
! 	self.graph = pyassem.PyFlowGraph("<module>", filename)
! 	self.super_init(filename)
  
  class FunctionCodeGenerator(CodeGenerator):
--- 670,676 ----
      
      def __init__(self, filename):
!         # XXX <module> is ? in compile.c
!         self.graph = pyassem.PyFlowGraph("<module>", filename)
!         self.super_init(filename)
  
  class FunctionCodeGenerator(CodeGenerator):
***************
*** 687,702 ****
          else:
              name = func.name
! 	args, hasTupleArg = generateArgList(func.argnames)
! 	self.graph = pyassem.PyFlowGraph(name, filename, args, 
! 					   optimized=1) 
! 	self.isLambda = isLambda
! 	self.super_init(filename)
  
          lnf = walk(func.code, LocalNameFinder(args), 0)
          self.locals.push(lnf.getLocals())
! 	if func.varargs:
! 	    self.graph.setFlag(CO_VARARGS)
! 	if func.kwargs:
! 	    self.graph.setFlag(CO_VARKEYWORDS)
          self.set_lineno(func)
          if hasTupleArg:
--- 687,702 ----
          else:
              name = func.name
!         args, hasTupleArg = generateArgList(func.argnames)
!         self.graph = pyassem.PyFlowGraph(name, filename, args, 
!                                            optimized=1) 
!         self.isLambda = isLambda
!         self.super_init(filename)
  
          lnf = walk(func.code, LocalNameFinder(args), 0)
          self.locals.push(lnf.getLocals())
!         if func.varargs:
!             self.graph.setFlag(CO_VARARGS)
!         if func.kwargs:
!             self.graph.setFlag(CO_VARKEYWORDS)
          self.set_lineno(func)
          if hasTupleArg:
***************
*** 704,711 ****
  
      def finish(self):
! 	self.graph.startExitBlock()
! 	if not self.isLambda:
! 	    self.emit('LOAD_CONST', None)
! 	self.emit('RETURN_VALUE')
  
      def generateArgUnpack(self, args):
--- 704,711 ----
  
      def finish(self):
!         self.graph.startExitBlock()
!         if not self.isLambda:
!             self.emit('LOAD_CONST', None)
!         self.emit('RETURN_VALUE')
  
      def generateArgUnpack(self, args):
***************
*** 715,742 ****
                  self.emit('LOAD_FAST', '.nested%d' % count)
                  count = count + 1
!                 self.unpackTuple(arg)
                          
!     def unpackTuple(self, tup):
!         self.emit('UNPACK_TUPLE', len(tup))
          for elt in tup:
              if type(elt) == types.TupleType:
!                 self.unpackTuple(elt)
              else:
                  self.emit('STORE_FAST', elt)
  
  class ClassCodeGenerator(CodeGenerator):
      super_init = CodeGenerator.__init__
  
      def __init__(self, klass, filename):
! 	self.graph = pyassem.PyFlowGraph(klass.name, filename,
! 					   optimized=0)
          self.super_init(filename)
          lnf = walk(klass.code, LocalNameFinder(), 0)
          self.locals.push(lnf.getLocals())
  
      def finish(self):
! 	self.graph.startExitBlock()
          self.emit('LOAD_LOCALS')
! 	self.emit('RETURN_VALUE')
  
  
--- 715,745 ----
                  self.emit('LOAD_FAST', '.nested%d' % count)
                  count = count + 1
!                 self.unpackSequence(arg)
                          
!     def unpackSequence(self, tup):
!         self.emit('UNPACK_SEQUENCE', len(tup))
          for elt in tup:
              if type(elt) == types.TupleType:
!                 self.unpackSequence(elt)
              else:
                  self.emit('STORE_FAST', elt)
  
+     unpackTuple = unpackSequence
+ 
  class ClassCodeGenerator(CodeGenerator):
      super_init = CodeGenerator.__init__
  
      def __init__(self, klass, filename):
!         self.graph = pyassem.PyFlowGraph(klass.name, filename,
!                                            optimized=0)
          self.super_init(filename)
          lnf = walk(klass.code, LocalNameFinder(), 0)
          self.locals.push(lnf.getLocals())
+         self.graph.setFlag(CO_NEWLOCALS)
  
      def finish(self):
!         self.graph.startExitBlock()
          self.emit('LOAD_LOCALS')
!         self.emit('RETURN_VALUE')
  
  
***************
*** 747,758 ****
      count = 0
      for elt in arglist:
! 	if type(elt) == types.StringType:
! 	    args.append(elt)
! 	elif type(elt) == types.TupleType:
! 	    args.append(TupleArg(count, elt))
! 	    count = count + 1
! 	    extra.extend(misc.flatten(elt))
! 	else:
! 	    raise ValueError, "unexpect argument type:", elt
      return args + extra, count
  
--- 750,761 ----
      count = 0
      for elt in arglist:
!         if type(elt) == types.StringType:
!             args.append(elt)
!         elif type(elt) == types.TupleType:
!             args.append(TupleArg(count, elt))
!             count = count + 1
!             extra.extend(misc.flatten(elt))
!         else:
!             raise ValueError, "unexpect argument type:", elt
      return args + extra, count
  
***************
*** 772,776 ****
  
      def visitDict(self, node):
! 	pass
  
      def visitGlobal(self, node):
--- 775,779 ----
  
      def visitDict(self, node):
!         pass
  
      def visitGlobal(self, node):
***************
*** 782,786 ****
  
      def visitLambda(self, node):
! 	pass
  
      def visitImport(self, node):
--- 785,789 ----
  
      def visitLambda(self, node):
!         pass
  
      def visitImport(self, node):
***************
*** 817,819 ****
  
      for file in sys.argv[1:]:
! 	compile(file)
--- 820,822 ----
  
      for file in sys.argv[1:]:
!         compile(file)

Index: visitor.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Tools/compiler/compiler/visitor.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -r1.3 -r1.4
*** visitor.py	2000/03/16 20:04:16	1.3
--- visitor.py	2000/08/12 20:32:46	1.4
***************
*** 39,43 ****
      def __init__(self):
          self.node = None
! 	self._cache = {}
  
      def preorder(self, tree, visitor):
--- 39,43 ----
      def __init__(self):
          self.node = None
!         self._cache = {}
  
      def preorder(self, tree, visitor):
***************
*** 48,52 ****
  
      def _preorder(self, node, *args):
! 	return apply(self.dispatch, (node,) + args)
  
      def default(self, node, *args):
--- 48,52 ----
  
      def _preorder(self, node, *args):
!         return apply(self.dispatch, (node,) + args)
  
      def default(self, node, *args):
***************
*** 57,65 ****
      def dispatch(self, node, *args):
          self.node = node
! 	meth = self._cache.get(node.__class__, None)
! 	className = node.__class__.__name__
! 	if meth is None:
! 	    meth = getattr(self.visitor, 'visit' + className, self.default)
! 	    self._cache[node.__class__] = meth
          if self.VERBOSE > 0:
              if self.VERBOSE == 1:
--- 57,65 ----
      def dispatch(self, node, *args):
          self.node = node
!         meth = self._cache.get(node.__class__, None)
!         className = node.__class__.__name__
!         if meth is None:
!             meth = getattr(self.visitor, 'visit' + className, self.default)
!             self._cache[node.__class__] = meth
          if self.VERBOSE > 0:
              if self.VERBOSE == 1:
***************
*** 68,72 ****
              else:
                  print "dispatch", className, (meth and meth.__name__ or '')
! 	return apply(meth, (node,) + args)
  
  class ExampleASTVisitor(ASTVisitor):
--- 68,72 ----
              else:
                  print "dispatch", className, (meth and meth.__name__ or '')
!         return apply(meth, (node,) + args)
  
  class ExampleASTVisitor(ASTVisitor):
***************
*** 81,89 ****
      def dispatch(self, node, *args):
          self.node = node
! 	meth = self._cache.get(node.__class__, None)
! 	className = node.__class__.__name__
! 	if meth is None:
! 	    meth = getattr(self.visitor, 'visit' + className, 0)
! 	    self._cache[node.__class__] = meth
          if self.VERBOSE > 1:
              print "dispatch", className, (meth and meth.__name__ or '')
--- 81,89 ----
      def dispatch(self, node, *args):
          self.node = node
!         meth = self._cache.get(node.__class__, None)
!         className = node.__class__.__name__
!         if meth is None:
!             meth = getattr(self.visitor, 'visit' + className, 0)
!             self._cache[node.__class__] = meth
          if self.VERBOSE > 1:
              print "dispatch", className, (meth and meth.__name__ or '')
***************
*** 93,105 ****
              klass = node.__class__
              if not self.examples.has_key(klass):
! 		self.examples[klass] = klass
! 		print
! 		print self.visitor
! 		print klass
! 		for attr in dir(node):
! 		    if attr[0] != '_':
! 			print "\t", "%-12.12s" % attr, getattr(node, attr)
! 		print
! 	    return apply(self.default, (node,) + args)
  
  _walker = ASTVisitor
--- 93,105 ----
              klass = node.__class__
              if not self.examples.has_key(klass):
!                 self.examples[klass] = klass
!                 print
!                 print self.visitor
!                 print klass
!                 for attr in dir(node):
!                     if attr[0] != '_':
!                         print "\t", "%-12.12s" % attr, getattr(node, attr)
!                 print
!             return apply(self.default, (node,) + args)
  
  _walker = ASTVisitor