[pypy-svn] r17565 - in pypy/dist/pypy/interpreter: astcompiler pyparser pyparser/test test

ac at codespeak.net ac at codespeak.net
Wed Sep 14 16:06:36 CEST 2005


Author: ac
Date: Wed Sep 14 16:06:35 2005
New Revision: 17565

Modified:
   pypy/dist/pypy/interpreter/astcompiler/consts.py
   pypy/dist/pypy/interpreter/astcompiler/pyassem.py
   pypy/dist/pypy/interpreter/astcompiler/pycodegen.py
   pypy/dist/pypy/interpreter/astcompiler/symbols.py
   pypy/dist/pypy/interpreter/pyparser/astbuilder.py
   pypy/dist/pypy/interpreter/pyparser/test/test_astcompiler.py
   pypy/dist/pypy/interpreter/test/test_compiler.py
Log:
(Arre, Samuele)
Port the scope ambiguity fix from stablecompiler and
make gen-exprs work (allmost).



Modified: pypy/dist/pypy/interpreter/astcompiler/consts.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/consts.py	(original)
+++ pypy/dist/pypy/interpreter/astcompiler/consts.py	Wed Sep 14 16:06:35 2005
@@ -8,7 +8,7 @@
 SC_FREE = 3
 SC_CELL = 4
 SC_UNKNOWN = 5
-SC_REALLY_GLOBAL = 6
+SC_DEFAULT = 6
 
 CO_OPTIMIZED = 0x0001
 CO_NEWLOCALS = 0x0002

Modified: pypy/dist/pypy/interpreter/astcompiler/pyassem.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/pyassem.py	(original)
+++ pypy/dist/pypy/interpreter/astcompiler/pyassem.py	Wed Sep 14 16:06:35 2005
@@ -434,7 +434,8 @@
 
 class PyFlowGraph(FlowGraph):
 
-    def __init__(self, space, name, filename, args=None, optimized=0, klass=0):
+    def __init__(self, space, name, filename, args=None, optimized=0,
+                 klass=0, newlocals=0):
         FlowGraph.__init__(self, space)
         if args is None:
             args = []
@@ -444,10 +445,12 @@
         self.args = args # XXX
         self.argcount = getArgCount(args)
         self.klass = klass
+        self.flags = 0
         if optimized:
-            self.flags = CO_OPTIMIZED | CO_NEWLOCALS
-        else:
-            self.flags = 0
+            self.flags |= CO_OPTIMIZED
+        if newlocals:
+            self.flags |= CO_NEWLOCALS
+
         self.consts = []
         self.names = []
         # Free variables found by the symbol table scan, including

Modified: pypy/dist/pypy/interpreter/astcompiler/pycodegen.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/pycodegen.py	(original)
+++ pypy/dist/pypy/interpreter/astcompiler/pycodegen.py	Wed Sep 14 16:06:35 2005
@@ -9,7 +9,7 @@
 from pypy.interpreter.astcompiler import ast, parse, walk, syntax
 from pypy.interpreter.astcompiler import pyassem, misc, future, symbols
 from pypy.interpreter.astcompiler.consts import SC_LOCAL, SC_GLOBAL, \
-    SC_FREE, SC_CELL, SC_REALLY_GLOBAL
+    SC_FREE, SC_CELL, SC_DEFAULT
 from pypy.interpreter.astcompiler.consts import CO_VARARGS, CO_VARKEYWORDS, \
     CO_NEWLOCALS, CO_NESTED, CO_GENERATOR, CO_GENERATOR_ALLOWED, CO_FUTURE_DIVISION
 from pypy.interpreter.astcompiler.pyassem import TupleArg
@@ -140,7 +140,7 @@
     """Defines basic code generator for Python bytecode
     """
 
-
+    scopeambiguity = False
 
     def __init__(self, space, graph):
         self.space = space
@@ -219,9 +219,19 @@
         self._nameOp('STORE', name)
 
     def loadName(self, name):
+        if (self.scope.nested and self.scopeambiguity and
+            name in self.scope.hasbeenfree):
+            raise SyntaxError("cannot reference variable '%s' because "
+                              "of ambiguity between "
+                              "scopes" % name)
+
         self._nameOp('LOAD', name)
 
     def delName(self, name):
+        scope = self.scope.check_name(name)
+        if scope == SC_CELL:
+            raise SyntaxError("can not delete variable '%s' "
+                              "referenced in nested scope" % name)
         self._nameOp('DELETE', name)
 
     def _nameOp(self, prefix, name):
@@ -233,14 +243,14 @@
             else:
                 self.emitop(prefix + '_FAST', name)
         elif scope == SC_GLOBAL:
-            if not self.optimized:
-                self.emitop(prefix + '_NAME', name)
-            else:
-                self.emitop(prefix + '_GLOBAL', name)
+            self.emitop(prefix + '_GLOBAL', name)
         elif scope == SC_FREE or scope == SC_CELL:
             self.emitop(prefix + '_DEREF', name)
-        elif scope == SC_REALLY_GLOBAL:
-            self.emitop(prefix +  '_GLOBAL', name)
+        elif scope == SC_DEFAULT:
+            if self.optimized and self.localsfullyknown:
+                self.emitop(prefix + '_GLOBAL', name)
+            else:
+                self.emitop(prefix + '_NAME', name)
         else:
             raise RuntimeError, "unsupported scope for var %s: %d" % \
                   (name, scope)
@@ -331,7 +341,8 @@
             ndecorators = 0
 
         gen = FunctionCodeGenerator(self.space, node, isLambda,
-                               self.class_name, self.get_module())
+                               self.class_name, self.get_module(),
+                                    self.scopeambiguity)
         walk(node.code, gen)
         gen.finish()
         self.set_lineno(node)
@@ -354,7 +365,8 @@
 
     def visitClass(self, node):
         gen = ClassCodeGenerator(self.space, node,
-                                 self.get_module())
+                                 self.get_module(),
+                                 self.scopeambiguity)
         walk(node.code, gen)
         gen.finish()
         self.set_lineno(node)
@@ -600,7 +612,7 @@
 
     def visitGenExpr(self, node):
         gen = GenExprCodeGenerator(self.space, node, self.class_name,
-                                   self.get_module())
+                                   self.get_module(), self.scopeambiguity)
         inner = node.code
         assert isinstance(inner, ast.GenExprInner)
         walk(inner, gen)
@@ -836,8 +848,6 @@
         self.emitop('IMPORT_NAME', node.modname)
         for name, alias in node.names:
             if name == '*':
-                if self.scope.nested:
-                    raise SyntaxError('import * is not allowed in a nested function')
                 self.namespace = 0
                 self.emit('IMPORT_STAR')
                 # There can only be one name w/ from ... import *
@@ -925,8 +935,6 @@
         }
 
     def visitExec(self, node):
-        if self.scope.nested and node.locals is None and node.globals is None:
-            raise SyntaxError('unqualified exec is not allowed in a nested function')
         node.expr.accept( self )
         if node.locals is None:
             self.emitop_obj('LOAD_CONST', self.space.w_None)
@@ -1205,21 +1213,20 @@
         node.expr.accept( self )
         self.emit('PRINT_EXPR')
 
-AbstractFunctionCodeLambdaCounter = misc.Counter(0)
-
 class AbstractFunctionCode(CodeGenerator):
     def __init__(self, space, func, isLambda, class_name, mod):
         self.class_name = class_name
         self.module = mod
         if isLambda:
-            name = "<lambda.%d>" % AbstractFunctionCodeLambdaCounter.next()
+            name = "<lambda>"
         else:
             assert isinstance(func, ast.Function)
             name = func.name
 
         args, hasTupleArg = generateArgList(func.argnames)
         graph = pyassem.PyFlowGraph(space, name, func.filename, args,
-                                         optimized=1)
+                                    optimized=self.localsfullyknown,
+                                    newlocals=1)
         self.isLambda = isLambda
         CodeGenerator.__init__(self, space, graph)
         self.optimized = 1
@@ -1270,10 +1277,13 @@
 
 class FunctionCodeGenerator(AbstractFunctionCode):
 
-    def __init__(self, space, func, isLambda, class_name, mod):
+    def __init__(self, space, func, isLambda, class_name, mod, parentscopeambiguity):
         assert func.scope is not None
         self.scope = func.scope
+        self.localsfullyknown = self.scope.localsfullyknown
+        self.scopeambiguity = (not self.localsfullyknown or parentscopeambiguity)
         AbstractFunctionCode.__init__(self, space, func, isLambda, class_name, mod)
+        
         self.graph.setFreeVars(self.scope.get_free_vars())
         self.graph.setCellVars(self.scope.get_cell_vars())
         if self.scope.generator:
@@ -1281,9 +1291,12 @@
 
 class GenExprCodeGenerator(AbstractFunctionCode):
 
-    def __init__(self, space, gexp, class_name, mod):
+    def __init__(self, space, gexp, class_name, mod, parentscopeambiguity):
         assert gexp.scope is not None
         self.scope = gexp.scope
+        self.localsfullyknown = self.scope.localsfullyknown
+        self.scopeambiguity = (not self.localsfullyknown or parentscopeambiguity)
+
         AbstractFunctionCode.__init__(self, space, gexp, 1, class_name, mod)
         self.graph.setFreeVars(self.scope.get_free_vars())
         self.graph.setCellVars(self.scope.get_cell_vars())
@@ -1296,6 +1309,7 @@
         self.module = module
         graph = pyassem.PyFlowGraph( space, klass.name, klass.filename,
                                            optimized=0, klass=1)
+
         CodeGenerator.__init__(self, space, graph)
         self.graph.setFlag(CO_NEWLOCALS)
         if not space.is_w(klass.doc, space.w_None):
@@ -1311,9 +1325,10 @@
 
 class ClassCodeGenerator(AbstractClassCode):
 
-    def __init__(self, space, klass, module):
+    def __init__(self, space, klass, module, parentscopeambiguity):
         assert klass.scope is not None
         self.scope = klass.scope
+        self.scopeambiguity = parentscopeambiguity
         AbstractClassCode.__init__(self, space, klass, module)
         self.graph.setFreeVars(self.scope.get_free_vars())
         self.graph.setCellVars(self.scope.get_cell_vars())

Modified: pypy/dist/pypy/interpreter/astcompiler/symbols.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/symbols.py	(original)
+++ pypy/dist/pypy/interpreter/astcompiler/symbols.py	Wed Sep 14 16:06:35 2005
@@ -2,7 +2,7 @@
 
 from pypy.interpreter.astcompiler import ast
 from pypy.interpreter.astcompiler.consts import SC_LOCAL, SC_GLOBAL, \
-    SC_FREE, SC_CELL, SC_UNKNOWN, SC_REALLY_GLOBAL
+    SC_FREE, SC_CELL, SC_UNKNOWN, SC_DEFAULT
 from pypy.interpreter.astcompiler.misc import mangle, Counter
 from pypy.interpreter.pyparser.error import SyntaxError
 import types
@@ -13,6 +13,7 @@
 MANGLE_LEN = 256
 
 class Scope:
+    localsfullyknown = True
     # XXX how much information do I need about each name?
     def __init__(self, name, module, klass=None):
         self.name = name
@@ -22,6 +23,7 @@
         self.globals = {}
         self.params = {}
         self.frees = {}
+        self.hasbeenfree = {}
         self.cells = {}
         self.children = []
         # nested is true if the class could contain free variables,
@@ -91,7 +93,7 @@
         The scope of a name could be LOCAL, GLOBAL, FREE, or CELL.
         """
         if name in self.globals:
-            return SC_REALLY_GLOBAL
+            return SC_GLOBAL
         if name in self.cells:
             return SC_CELL
         if name in self.defs:
@@ -102,7 +104,7 @@
         if self.nested:
             return SC_UNKNOWN
         else:
-            return SC_GLOBAL
+            return SC_DEFAULT
 
     def get_free_vars(self):
         if not self.nested:
@@ -113,6 +115,7 @@
             if not (name in self.defs or
                     name in self.globals):
                 free[name] = 1
+        self.hasbeenfree.update(free)
         return free.keys()
 
     def handle_children(self):
@@ -135,7 +138,8 @@
         Be careful to stop if a child does not think the name is
         free.
         """
-        self.globals[name] = 1
+        if name not in self.defs:
+            self.globals[name] = 1
         if name in self.frees:
             del self.frees[name]
         for child in self.children:
@@ -156,7 +160,7 @@
                 if sc == SC_UNKNOWN or sc == SC_FREE \
                    or isinstance(self, ClassScope):
                     self.frees[name] = 1
-                elif sc == SC_GLOBAL or sc == SC_REALLY_GLOBAL:
+                elif sc == SC_DEFAULT or sc == SC_GLOBAL:
                     child_globals.append(name)
                 elif isinstance(self, FunctionScope) and sc == SC_LOCAL:
                     self.cells[name] = 1
@@ -262,6 +266,12 @@
         self.pop_scope()
         self.handle_free_vars(scope, parent)
 
+    def visitExec(self, node):
+        if not (node.globals or node.locals):
+            parent = self.cur_scope()
+            parent.localsfullyknown = False # bare exec statement
+        ast.ASTVisitor.visitExec(self, node)
+
     def visitGenExpr(self, node ):
         parent = self.cur_scope()
         scope = GenExprScope(self.module, self.klass);
@@ -375,6 +385,7 @@
         scope = self.cur_scope()
         for name, asname in node.names:
             if name == "*":
+                scope.localsfullyknown = False
                 continue
             scope.add_def(asname or name)
 

Modified: pypy/dist/pypy/interpreter/pyparser/astbuilder.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/astbuilder.py	(original)
+++ pypy/dist/pypy/interpreter/pyparser/astbuilder.py	Wed Sep 14 16:06:35 2005
@@ -795,6 +795,7 @@
         # GenExpr(GenExprInner(Name('i'), [GenExprFor(AssName('i', 'OP_ASSIGN'), Name('j'), [])])))]))
         expr = atoms[0]
         genexpr_for = parse_genexpr_for(atoms[1:])
+        genexpr_for[0].is_outmost = True
         builder.push(ast.GenExpr(ast.GenExprInner(expr, genexpr_for)))
         return
     builder.push(ast.Tuple(items))

Modified: pypy/dist/pypy/interpreter/pyparser/test/test_astcompiler.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/test/test_astcompiler.py	(original)
+++ pypy/dist/pypy/interpreter/pyparser/test/test_astcompiler.py	Wed Sep 14 16:06:35 2005
@@ -31,7 +31,7 @@
      listmakers,
      dictmakers,
      multiexpr,
-     # genexps, investigate?
+     genexps,
      attraccess,
      slices,
      imports,

Modified: pypy/dist/pypy/interpreter/test/test_compiler.py
==============================================================================
--- pypy/dist/pypy/interpreter/test/test_compiler.py	(original)
+++ pypy/dist/pypy/interpreter/test/test_compiler.py	Wed Sep 14 16:06:35 2005
@@ -197,12 +197,6 @@
     def setup_method(self, method):
         self.compiler = PythonAstCompiler(self.space)
 
-    def test_scope_importstar_with_nested_free(self):
-        py.test.skip("INPROGESS")
-
-    def test_scope_exec_with_nested_free(self):
-        py.test.skip("INPROGESS")
-
 class SkippedForNowTestPyPyCompiler(BaseTestCompiler):
     def setup_method(self, method):
         self.compiler = PyPyCompiler(self.space)



More information about the Pypy-commit mailing list