[Python-checkins] CVS: python/dist/src/Tools/compiler/compiler pyassem.py,1.16,1.17 pycodegen.py,1.31,1.32 symbols.py,1.3,1.4

Jeremy Hylton jhylton@users.sourceforge.net
Wed, 11 Apr 2001 23:40:44 -0700


Update of /cvsroot/python/python/dist/src/Tools/compiler/compiler
In directory usw-pr-cvs1:/tmp/cvs-serv6499

Modified Files:
	pyassem.py pycodegen.py symbols.py 
Log Message:
Preliminary support for nested scopes

XXX Still doesn't work right for classes
XXX Still doesn't do sufficient error checking


Index: pyassem.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Tools/compiler/compiler/pyassem.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -C2 -r1.16 -r1.17
*** pyassem.py	2001/04/11 16:21:51	1.16
--- pyassem.py	2001/04/12 06:40:42	1.17
***************
*** 100,109 ****
              order.append(self.exit)
  
- ##        for b in order:
- ##            print repr(b)
- ##            print "\t", b.get_children()
- ##            print b
- ##            print
-             
          return order
  
--- 100,103 ----
***************
*** 223,226 ****
--- 217,221 ----
  CO_VARARGS = 0x0004
  CO_VARKEYWORDS = 0x0008
+ CO_NESTED = 0x0010
  
  # the FlowGraph is transformed in place; it exists in one of these states
***************
*** 246,249 ****
--- 241,253 ----
          self.consts = []
          self.names = []
+         # Free variables found by the symbol table scan, including
+         # variables used only in nested scopes, are included here.
+         self.freevars = []
+         self.cellvars = []
+         # The closure list is used to track the order of cell
+         # variables and free variables in the resulting code object.
+         # The offsets used by LOAD_CLOSURE/LOAD_DEREF refer to both
+         # kinds of variables.
+         self.closure = []
          self.varnames = list(args) or []
          for i in range(len(self.varnames)):
***************
*** 261,264 ****
--- 265,274 ----
              self.argcount = self.argcount - 1
  
+     def setFreeVars(self, names):
+         self.freevars = list(names)
+ 
+     def setCellVars(self, names):
+         self.cellvars = names
+ 
      def getCode(self):
          """Get a Python code object"""
***************
*** 336,339 ****
--- 346,350 ----
          assert self.stage == FLAT
          self.consts.insert(0, self.docstring)
+         self.sort_cellvars()
          for i in range(len(self.insts)):
              t = self.insts[i]
***************
*** 346,349 ****
--- 357,373 ----
          self.stage = CONV
  
+     def sort_cellvars(self):
+         """Sort cellvars in the order of varnames and prune from freevars.
+         """
+         cells = {}
+         for name in self.cellvars:
+             cells[name] = 1
+         self.cellvars = [name for name in self.varnames
+                          if cells.has_key(name)]
+         for name in self.cellvars:
+             del cells[name]
+         self.cellvars = self.cellvars + cells.keys()
+         self.closure = self.cellvars + self.freevars
+ 
      def _lookupName(self, name, list):
          """Return index of name in list, appending if necessary"""
***************
*** 383,386 ****
--- 407,421 ----
      _convert_DELETE_GLOBAL = _convert_NAME
  
+     def _convert_DEREF(self, arg):
+         self._lookupName(arg, self.names)
+         self._lookupName(arg, self.varnames)
+         return self._lookupName(arg, self.closure)
+     _convert_LOAD_DEREF = _convert_DEREF
+     _convert_STORE_DEREF = _convert_DEREF
+ 
+     def _convert_LOAD_CLOSURE(self, arg):
+         self._lookupName(arg, self.varnames)
+         return self._lookupName(arg, self.closure)
+ 
      _cmp = list(dis.cmp_op)
      def _convert_COMPARE_OP(self, arg):
***************
*** 433,437 ****
                          tuple(self.names), tuple(self.varnames),
                          self.filename, self.name, self.lnotab.firstline,
!                         self.lnotab.getTable())
  
      def getConsts(self):
--- 468,473 ----
                          tuple(self.names), tuple(self.varnames),
                          self.filename, self.name, self.lnotab.firstline,
!                         self.lnotab.getTable(), tuple(self.freevars),
!                         tuple(self.cellvars))
  
      def getConsts(self):

Index: pycodegen.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Tools/compiler/compiler/pycodegen.py,v
retrieving revision 1.31
retrieving revision 1.32
diff -C2 -r1.31 -r1.32
*** pycodegen.py	2001/04/11 16:43:13	1.31
--- pycodegen.py	2001/04/12 06:40:42	1.32
***************
*** 10,15 ****
  
  from compiler import ast, parse, walk
! from compiler import pyassem, misc, future
! from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS, TupleArg
  
  # Do we have Python 1.x or Python 2.x?
--- 10,17 ----
  
  from compiler import ast, parse, walk
! from compiler import pyassem, misc, future, symbols
! from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL
! from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,\
!      CO_NESTED, TupleArg
  
  # Do we have Python 1.x or Python 2.x?
***************
*** 47,51 ****
          root, filename = os.path.split(self.filename)
          if "nested_scopes" in future.find_futures(tree):
!             gen = NestedScopeCodeGenerator(filename)
          else:
              gen = ModuleCodeGenerator(filename)
--- 49,53 ----
          root, filename = os.path.split(self.filename)
          if "nested_scopes" in future.find_futures(tree):
!             gen = NestedScopeModuleCodeGenerator(filename)
          else:
              gen = ModuleCodeGenerator(filename)
***************
*** 71,82 ****
          return self.MAGIC + mtime
  
  class CodeGenerator:
  
      optimized = 0 # is namespace access optimized?
  
      def __init__(self, filename):
! ## Subclasses must define a constructor that intializes self.graph
! ## before calling this init function, e.g.
! ##         self.graph = pyassem.PyFlowGraph()
          self.filename = filename
          self.locals = misc.Stack()
--- 73,141 ----
          return self.MAGIC + mtime
  
+ class LocalNameFinder:
+     """Find local names in scope"""
+     def __init__(self, names=()):
+         self.names = misc.Set()
+         self.globals = misc.Set()
+         for name in names:
+             self.names.add(name)
+ 
+     # XXX list comprehensions and for loops
+ 
+     def getLocals(self):
+         for elt in self.globals.elements():
+             if self.names.has_elt(elt):
+                 self.names.remove(elt)
+         return self.names
+ 
+     def visitDict(self, node):
+         pass
+ 
+     def visitGlobal(self, node):
+         for name in node.names:
+             self.globals.add(name)
+ 
+     def visitFunction(self, node):
+         self.names.add(node.name)
+ 
+     def visitLambda(self, node):
+         pass
+ 
+     def visitImport(self, node):
+         for name, alias in node.names:
+             self.names.add(alias or name)
+ 
+     def visitFrom(self, node):
+         for name, alias in node.names:
+             self.names.add(alias or name)
+ 
+     def visitClass(self, node):
+         self.names.add(node.name)
+ 
+     def visitAssName(self, node):
+         self.names.add(node.name)
+ 
  class CodeGenerator:
+     """Defines basic code generator for Python bytecode
  
+     This class is an abstract base class.  Concrete subclasses must
+     define an __init__() that defines self.graph and then calls the
+     __init__() defined in this class.
+ 
+     The concrete class must also define the class attributes
+     NameFinder, FunctionGen, and ClassGen.  These attributes can be
+     defined in the initClass() method, which is a hook for
+     initializing these methods after all the classes have been
+     defined. 
+     """
+ 
      optimized = 0 # is namespace access optimized?
+     __initialized = None
  
      def __init__(self, filename):
!         if self.__initialized is None:
!             self.initClass()
!             self.__class__.__initialized = 1
!         self.checkClass()
          self.filename = filename
          self.locals = misc.Stack()
***************
*** 87,90 ****
--- 146,163 ----
          self._setupGraphDelegation()
  
+     def initClass(self):
+         """This method is called once for each class"""
+ 
+     def checkClass(self):
+         """Verify that class is constructed correctly"""
+         try:
+             assert hasattr(self, 'graph')
+             assert getattr(self, 'NameFinder')
+             assert getattr(self, 'FunctionGen')
+             assert getattr(self, 'ClassGen')
+         except AssertionError, msg:
+             intro = "Bad class construction for %s" % self.__class__.__name__
+             raise AssertionError, intro
+ 
      def _setupGraphDelegation(self):
          self.emit = self.graph.emit
***************
*** 140,147 ****
  
      # The first few visitor methods handle nodes that generator new
!     # code objects 
  
      def visitModule(self, node):
!         lnf = walk(node.node, LocalNameFinder(), 0)
          self.locals.push(lnf.getLocals())
          if node.doc:
--- 213,225 ----
  
      # The first few visitor methods handle nodes that generator new
!     # code objects.  They use class attributes to determine what
!     # specialized code generators to use.
! 
!     NameFinder = LocalNameFinder
!     FunctionGen = None
!     ClassGen = None
  
      def visitModule(self, node):
!         lnf = walk(node.node, self.NameFinder(), 0)
          self.locals.push(lnf.getLocals())
          if node.doc:
***************
*** 160,165 ****
          self._visitFuncOrLambda(node, isLambda=1)
  
!     def _visitFuncOrLambda(self, node, isLambda):
!         gen = FunctionCodeGenerator(node, self.filename, isLambda)
          walk(node.code, gen)
          gen.finish()
--- 238,243 ----
          self._visitFuncOrLambda(node, isLambda=1)
  
!     def _visitFuncOrLambda(self, node, isLambda=0):
!         gen = self.FunctionGen(node, self.filename, self.scopes, isLambda)
          walk(node.code, gen)
          gen.finish()
***************
*** 171,175 ****
  
      def visitClass(self, node):
!         gen = ClassCodeGenerator(node, self.filename)
          if node.doc:
              self.fixDocstring(node.code)
--- 249,253 ----
  
      def visitClass(self, node):
!         gen = self.ClassGen(node, self.filename, self.scopes)
          if node.doc:
              self.fixDocstring(node.code)
***************
*** 181,185 ****
              self.visit(base)
          self.emit('BUILD_TUPLE', len(node.bases))
!         self.emit('LOAD_CONST', gen.getCode())
          self.emit('MAKE_FUNCTION', 0)
          self.emit('CALL_FUNCTION', 0)
--- 259,263 ----
              self.visit(base)
          self.emit('BUILD_TUPLE', len(node.bases))
!         self.emit('LOAD_CONST', gen)
          self.emit('MAKE_FUNCTION', 0)
          self.emit('CALL_FUNCTION', 0)
***************
*** 884,915 ****
              self.emit('STORE_SUBSCR')
  
! class ModuleCodeGenerator(CodeGenerator):
!     __super_init = CodeGenerator.__init__
      __super_visitModule = CodeGenerator.visitModule
!     
!     def __init__(self, filename):
!         # XXX <module> is ? in compile.c
!         self.graph = pyassem.PyFlowGraph("<module>", filename)
!         self.__super_init(filename)
!         self.symbols = None
  
      def visitModule(self, node):
!         self.symbols = self.parseSymbols(node)
          self.__super_visitModule(node)
  
!     def parseSymbols(self, node):
!         # XXX not implemented
!         return None
  
! class NestedScopeCodeGenerator(ModuleCodeGenerator):
!     pass
  
! class FunctionCodeGenerator(CodeGenerator):
!     super_init = CodeGenerator.__init__
  
      optimized = 1
      lambdaCount = 0
  
!     def __init__(self, func, filename, isLambda=0):
          if isLambda:
              klass = FunctionCodeGenerator
--- 962,1073 ----
              self.emit('STORE_SUBSCR')
  
! class NestedScopeCodeGenerator(CodeGenerator):
      __super_visitModule = CodeGenerator.visitModule
!     __super_visitClass = CodeGenerator.visitClass
!     __super__visitFuncOrLambda = CodeGenerator._visitFuncOrLambda
! 
!     def parseSymbols(self, tree):
!         s = symbols.SymbolVisitor()
!         walk(tree, s)
!         return s.scopes
  
      def visitModule(self, node):
!         self.scopes = self.parseSymbols(node)
!         self.scope = self.scopes[node]
          self.__super_visitModule(node)
  
!     def _nameOp(self, prefix, name):
!         scope = self.scope.check_name(name)
!         if scope == SC_LOCAL:
!             if not self.optimized:
!                 self.emit(prefix + '_NAME', name)
!             else:
!                 self.emit(prefix + '_FAST', name)
!         elif scope == SC_GLOBAL:
!             self.emit(prefix + '_GLOBAL', name)
!         elif scope == SC_FREE or scope == SC_CELL:
!             self.emit(prefix + '_DEREF', name)
!         else:
!             raise RuntimeError, "unsupported scope for var %s: %d" % \
!                   (name, scope)
  
!     def _visitFuncOrLambda(self, node, isLambda=0):
!         gen = self.FunctionGen(node, self.filename, self.scopes, isLambda)
!         walk(node.code, gen)
!         gen.finish()
!         self.set_lineno(node)
!         for default in node.defaults:
!             self.visit(default)
!         frees = gen.scope.get_free_vars()
!         if frees:
!             for name in frees:
!                 self.emit('LOAD_CLOSURE', name)
!             self.emit('LOAD_CONST', gen)
!             self.emit('MAKE_CLOSURE', len(node.defaults))
!         else:
!             self.emit('LOAD_CONST', gen)
!             self.emit('MAKE_FUNCTION', len(node.defaults))
  
!     def visitClass(self, node):
!         gen = self.ClassGen(node, self.filename, self.scopes)
!         if node.doc:
!             self.fixDocstring(node.code)
!         walk(node.code, gen)
!         gen.finish()
!         self.set_lineno(node)
!         self.emit('LOAD_CONST', node.name)
!         for base in node.bases:
!             self.visit(base)
!         self.emit('BUILD_TUPLE', len(node.bases))
!         frees = gen.scope.get_free_vars()
!         for name in frees:
!             self.emit('LOAD_CLOSURE', name)
!         self.emit('LOAD_CONST', gen)
!         if frees:
!             self.emit('MAKE_CLOSURE', 0)
!         else:
!             self.emit('MAKE_FUNCTION', 0)
!         self.emit('CALL_FUNCTION', 0)
!         self.emit('BUILD_CLASS')
!         self.storeName(node.name)
!         
! 
! class LGBScopeMixin:
!     """Defines initClass() for Python 2.1-compatible scoping"""
!     def initClass(self):
!         self.__class__.NameFinder = LocalNameFinder
!         self.__class__.FunctionGen = FunctionCodeGenerator
!         self.__class__.ClassGen = ClassCodeGenerator
! 
! class NestedScopeMixin:
!     """Defines initClass() for nested scoping (Python 2.2-compatible)"""
!     def initClass(self):
!         self.__class__.NameFinder = LocalNameFinder
!         self.__class__.FunctionGen = NestedFunctionCodeGenerator
!         self.__class__.ClassGen = NestedClassCodeGenerator
! 
! class ModuleCodeGenerator(LGBScopeMixin, CodeGenerator):
!     __super_init = CodeGenerator.__init__
! 
!     scopes = None
!     
!     def __init__(self, filename):
!         self.graph = pyassem.PyFlowGraph("<module>", filename)
!         self.__super_init(filename)
! 
! class NestedScopeModuleCodeGenerator(NestedScopeMixin,
!                                      NestedScopeCodeGenerator):
!     __super_init = CodeGenerator.__init__
!     
!     def __init__(self, filename):
!         self.graph = pyassem.PyFlowGraph("<module>", filename)
!         self.__super_init(filename)
!         self.graph.setFlag(CO_NESTED)
  
+ class AbstractFunctionCode:
      optimized = 1
      lambdaCount = 0
  
!     def __init__(self, func, filename, scopes, isLambda):
          if isLambda:
              klass = FunctionCodeGenerator
***************
*** 927,931 ****
              self.setDocstring(func.doc)
  
!         lnf = walk(func.code, LocalNameFinder(args), 0)
          self.locals.push(lnf.getLocals())
          if func.varargs:
--- 1085,1089 ----
              self.setDocstring(func.doc)
  
!         lnf = walk(func.code, self.NameFinder(args), 0)
          self.locals.push(lnf.getLocals())
          if func.varargs:
***************
*** 964,975 ****
      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)
--- 1122,1151 ----
      unpackTuple = unpackSequence
  
! class FunctionCodeGenerator(LGBScopeMixin, AbstractFunctionCode,
!                             CodeGenerator): 
!     super_init = CodeGenerator.__init__ # call be other init
!     scopes = None
! 
! class NestedFunctionCodeGenerator(AbstractFunctionCode,
!                                   NestedScopeMixin,
!                                   NestedScopeCodeGenerator):
!     super_init = NestedScopeCodeGenerator.__init__ # call be other init
!     __super_init = AbstractFunctionCode.__init__
! 
!     def __init__(self, func, filename, scopes, isLambda):
!         self.scopes = scopes
!         self.scope = scopes[func]
!         self.__super_init(func, filename, scopes, isLambda)
!         self.graph.setFreeVars(self.scope.get_free_vars())
!         self.graph.setCellVars(self.scope.get_cell_vars())
!         self.graph.setFlag(CO_NESTED)
! 
! class AbstractClassCode:
  
!     def __init__(self, klass, filename, scopes):
          self.graph = pyassem.PyFlowGraph(klass.name, filename,
                                             optimized=0)
          self.super_init(filename)
!         lnf = walk(klass.code, self.NameFinder(), 0)
          self.locals.push(lnf.getLocals())
          self.graph.setFlag(CO_NEWLOCALS)
***************
*** 982,985 ****
--- 1158,1179 ----
          self.emit('RETURN_VALUE')
  
+ class ClassCodeGenerator(LGBScopeMixin, AbstractClassCode, CodeGenerator):
+     super_init = CodeGenerator.__init__
+     scopes = None
+ 
+ class NestedClassCodeGenerator(AbstractClassCode,
+                                NestedScopeMixin,
+                                NestedScopeCodeGenerator):
+     super_init = NestedScopeCodeGenerator.__init__ # call be other init
+     __super_init = AbstractClassCode.__init__
+ 
+     def __init__(self, klass, filename, scopes):
+         self.scopes = scopes
+         self.scope = scopes[klass]
+         self.__super_init(klass, filename, scopes)
+         self.graph.setFreeVars(self.scope.get_free_vars())
+         self.graph.setCellVars(self.scope.get_cell_vars())
+         self.graph.setFlag(CO_NESTED)
+ 
  def generateArgList(arglist):
      """Generate an arg list marking TupleArgs"""
***************
*** 997,1043 ****
              raise ValueError, "unexpect argument type:", elt
      return args + extra, count
- 
- class LocalNameFinder:
-     """Find local names in scope"""
-     def __init__(self, names=()):
-         self.names = misc.Set()
-         self.globals = misc.Set()
-         for name in names:
-             self.names.add(name)
- 
-     # XXX list comprehensions and for loops
- 
-     def getLocals(self):
-         for elt in self.globals.elements():
-             if self.names.has_elt(elt):
-                 self.names.remove(elt)
-         return self.names
- 
-     def visitDict(self, node):
-         pass
- 
-     def visitGlobal(self, node):
-         for name in node.names:
-             self.globals.add(name)
- 
-     def visitFunction(self, node):
-         self.names.add(node.name)
- 
-     def visitLambda(self, node):
-         pass
- 
-     def visitImport(self, node):
-         for name, alias in node.names:
-             self.names.add(alias or name)
- 
-     def visitFrom(self, node):
-         for name, alias in node.names:
-             self.names.add(alias or name)
- 
-     def visitClass(self, node):
-         self.names.add(node.name)
- 
-     def visitAssName(self, node):
-         self.names.add(node.name)
  
  def findOp(node):
--- 1191,1194 ----

Index: symbols.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Tools/compiler/compiler/symbols.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -r1.3 -r1.4
*** symbols.py	2001/04/09 20:11:59	1.3
--- symbols.py	2001/04/12 06:40:42	1.4
***************
*** 2,7 ****
--- 2,10 ----
  
  from compiler import ast
+ from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL, SC_UNKNOWN
  import types
  
+ import sys
+ 
  MANGLE_LEN = 256
  
***************
*** 15,19 ****
--- 18,27 ----
          self.globals = {}
          self.params = {}
+         self.frees = {}
+         self.cells = {}
          self.children = []
+         # nested is true if the class could contain free variables,
+         # i.e. if it is nested within another function.
+         self.nested = None
          self.klass = None
          if klass is not None:
***************
*** 71,74 ****
--- 79,178 ----
          return self.children
  
+     def DEBUG(self):
+         return
+         print >> sys.stderr, self.name, self.nested and "nested" or ""
+         print >> sys.stderr, "\tglobals: ", self.globals
+         print >> sys.stderr, "\tcells: ", self.cells
+         print >> sys.stderr, "\tdefs: ", self.defs
+         print >> sys.stderr, "\tuses: ", self.uses
+         print >> sys.stderr, "\tfrees:", self.frees
+ 
+     def check_name(self, name):
+         """Return scope of name.
+ 
+         The scope of a name could be LOCAL, GLOBAL, FREE, or CELL.
+         """
+         if self.globals.has_key(name):
+             return SC_GLOBAL
+         if self.cells.has_key(name):
+             return SC_CELL
+         if self.defs.has_key(name):
+             return SC_LOCAL
+         if self.nested and (self.frees.has_key(name) or
+                             self.uses.has_key(name)):
+             return SC_FREE
+         if self.nested:
+             return SC_UNKNOWN
+         else:
+             return SC_GLOBAL
+ 
+     def get_free_vars(self):
+         if not self.nested:
+             return ()
+         free = {}
+         free.update(self.frees)
+         for name in self.uses.keys():
+             if not (self.defs.has_key(name) or
+                     self.globals.has_key(name)):
+                 free[name] = 1
+         return free.keys()
+ 
+     def handle_children(self):
+         for child in self.children:
+             frees = child.get_free_vars()
+             globals = self.add_frees(frees)
+             for name in globals:
+                 child.force_global(name)
+ 
+     def force_global(self, name):
+         """Force name to be global in scope.
+ 
+         Some child of the current node had a free reference to name.
+         When the child was processed, it was labelled a free
+         variable.  Now that all its enclosing scope have been
+         processed, the name is known to be a global or builtin.  So
+         walk back down the child chain and set the name to be global
+         rather than free.
+ 
+         Be careful to stop if a child does not think the name is
+         free. 
+         """
+         self.globals[name] = 1
+         if self.frees.has_key(name):
+             del self.frees[name]
+         for child in self.children:
+             if child.check_name(name) == SC_FREE:
+                 child.force_global(name)
+ 
+     def add_frees(self, names):
+         """Process list of free vars from nested scope.
+ 
+         Returns a list of names that are either 1) declared global in the
+         parent or 2) undefined in a top-level parent.  In either case,
+         the nested scope should treat them as globals.
+         """
+         child_globals = []
+         for name in names:
+             sc = self.check_name(name)
+             if self.nested:
+                 if sc == SC_UNKNOWN or sc == SC_FREE \
+                    or isinstance(self, ClassScope):
+                     self.frees[name] = 1
+                 elif sc == SC_GLOBAL:
+                     child_globals.append(name)
+                 elif isinstance(self, FunctionScope) and sc == SC_LOCAL:
+                     self.cells[name] = 1
+                 else:
+                     child_globals.append(name)
+             else:
+                 if sc == SC_LOCAL:
+                     self.cells[name] = 1
+                 else:
+                     child_globals.append(name)
+         return child_globals
+ 
+     def get_cell_vars(self):
+         return self.cells.keys()
+ 
  class ModuleScope(Scope):
      __super_init = Scope.__init__
***************
*** 76,81 ****
      def __init__(self):
          self.__super_init("global", self)
  
! class LambdaScope(Scope):
      __super_init = Scope.__init__
  
--- 180,188 ----
      def __init__(self):
          self.__super_init("global", self)
+ 
+ class FunctionScope(Scope):
+     pass
  
! class LambdaScope(FunctionScope):
      __super_init = Scope.__init__
  
***************
*** 87,93 ****
          self.__super_init("lambda.%d" % i, module, klass)
  
- class FunctionScope(Scope):
-     pass
- 
  class ClassScope(Scope):
      __super_init = Scope.__init__
--- 194,197 ----
***************
*** 112,126 ****
              self.visit(n, parent)
          scope = FunctionScope(node.name, self.module, self.klass)
          self.scopes[node] = scope
          self._do_args(scope, node.argnames)
          self.visit(node.code, scope)
! 
      def visitLambda(self, node, parent):
          for n in node.defaults:
              self.visit(n, parent)
          scope = LambdaScope(self.module, self.klass)
          self.scopes[node] = scope
          self._do_args(scope, node.argnames)
          self.visit(node.code, scope)
  
      def _do_args(self, scope, args):
--- 216,237 ----
              self.visit(n, parent)
          scope = FunctionScope(node.name, self.module, self.klass)
+         if parent.nested or isinstance(parent, FunctionScope):
+             scope.nested = 1
          self.scopes[node] = scope
          self._do_args(scope, node.argnames)
          self.visit(node.code, scope)
!         self.handle_free_vars(scope, parent)
!         scope.DEBUG()
!         
      def visitLambda(self, node, parent):
          for n in node.defaults:
              self.visit(n, parent)
          scope = LambdaScope(self.module, self.klass)
+         if parent.nested or isinstance(parent, FunctionScope):
+             scope.nested = 1
          self.scopes[node] = scope
          self._do_args(scope, node.argnames)
          self.visit(node.code, scope)
+         self.handle_free_vars(scope, parent)
  
      def _do_args(self, scope, args):
***************
*** 131,134 ****
--- 242,251 ----
                  scope.add_param(name)
  
+     def handle_free_vars(self, scope, parent):
+         parent.add_child(scope)
+         if scope.children:
+             scope.DEBUG()
+         scope.handle_children()
+ 
      def visitClass(self, node, parent):
          parent.add_def(node.name)
***************
*** 136,139 ****
--- 253,258 ----
              self.visit(n, parent)
          scope = ClassScope(node.name, self.module)
+         if parent.nested or isinstance(parent, FunctionScope):
+             scope.nested = 1
          self.scopes[node] = scope
          prev = self.klass
***************
*** 141,144 ****
--- 260,264 ----
          self.visit(node.code, scope)
          self.klass = prev
+         self.handle_free_vars(scope, parent)
  
      # name can be a def or a use