manual merge of Haoyu's nonlocal implementation
authorStefan Behnel <scoder@users.berlios.de>
Thu, 16 Dec 2010 23:19:19 +0000 (00:19 +0100)
committerStefan Behnel <scoder@users.berlios.de>
Thu, 16 Dec 2010 23:19:19 +0000 (00:19 +0100)
Cython/Compiler/Nodes.py
Cython/Compiler/Parsing.pxd
Cython/Compiler/Parsing.py
Cython/Compiler/Scanning.py
Cython/Compiler/Symtab.py
Cython/Compiler/TypeInference.py
tests/run/nonlocal_T490.pyx [new file with mode: 0644]

index 39539cadc82ceaeb4c61ab547cdec3cedbbdb641..5ab4e598a39570e45885dd470f6a61f5f1ef6030 100644 (file)
@@ -3434,6 +3434,24 @@ class GlobalNode(StatNode):
         pass
 
 
+class NonlocalNode(StatNode):
+    # Nonlocal variable declaration via the 'nonlocal' keyword.
+    #
+    # names    [string]
+
+    child_attrs = []
+
+    def analyse_declarations(self, env):
+        for name in self.names:
+            env.declare_nonlocal(name, self.pos)
+
+    def analyse_expressions(self, env):
+        pass
+
+    def generate_execution_code(self, code):
+        pass
+
+
 class ExprStatNode(StatNode):
     #  Expression used as a statement.
     #
index 3c64665261e6402ea5e5fe5cbb0c2cbd2288d042..5d479b2a880bf8fdf9214b69d6ca1cac4df70bcf 100644 (file)
@@ -84,6 +84,7 @@ cdef p_genexp(PyrexScanner s, expr)
 #-------------------------------------------------------
 
 cdef p_global_statement(PyrexScanner s)
+cdef p_nonlocal_statement(PyrexScanner s)
 cdef p_expression_or_assignment(PyrexScanner s)
 cdef p_print_statement(PyrexScanner s)
 cdef p_exec_statement(PyrexScanner s)
index a98758a83d9bffdf4f88c25c94fb923b38ea40b7..deefa5ba6cd6b6e5b38614df649d65e3cdc31490 100644 (file)
@@ -1047,6 +1047,12 @@ def p_global_statement(s):
     names = p_ident_list(s)
     return Nodes.GlobalNode(pos, names = names)
 
+def p_nonlocal_statement(s):
+    pos = s.position()
+    s.next()
+    names = p_ident_list(s)
+    return Nodes.NonlocalNode(pos, names = names)
+
 def p_expression_or_assignment(s):
     expr_list = [p_testlist_star_expr(s)]
     while s.sy == '=':
@@ -1600,6 +1606,8 @@ def p_simple_statement(s, first_statement = 0):
     #print "p_simple_statement:", s.sy, s.systring ###
     if s.sy == 'global':
         node = p_global_statement(s)
+    elif s.sy == 'nonlocal':
+        node = p_nonlocal_statement(s)
     elif s.sy == 'print':
         node = p_print_statement(s)
     elif s.sy == 'exec':
index 232e82eae3c07bc8a7adfaf813b07a373bfd06cc..d9f2586ed6cd110705a78f93de5ed3413f190617 100644 (file)
@@ -36,7 +36,7 @@ def get_lexicon():
 #------------------------------------------------------------------
 
 py_reserved_words = [
-    "global", "def", "class", "print", "del", "pass", "break",
+    "global", "nonlocal", "def", "class", "print", "del", "pass", "break",
     "continue", "return", "raise", "import", "exec", "try",
     "except", "finally", "while", "if", "elif", "else", "for",
     "in", "assert", "and", "or", "not", "is", "in", "lambda",
index 068e99e101f882600933a3ebe4fbb41fb71fd99b..5bbad5072ae3277077a3eb78aa2bef211db219ae 100644 (file)
@@ -1271,6 +1271,15 @@ class LocalScope(Scope):
         else:
             entry = self.global_scope().lookup_target(name)
             self.entries[name] = entry
+
+    def declare_nonlocal(self, name, pos):
+        # Pull entry from outer scope into local scope
+        if self.lookup_here(name):
+            warning(pos, "'%s' redeclared" % name, 0)
+        else:
+            entry = self.lookup(name)
+            if entry is None or not entry.from_closure:
+                error(pos, "no binding for nonlocal '%s' found" % name)
         
     def lookup(self, name):
         # Look up name in this scope or an enclosing one.
index 0cf100ad4862e55b02d4e8895ddebb80db327231..e50f7648d9ff18880de728afa11449f38f6ea004 100644 (file)
@@ -215,7 +215,8 @@ class SimpleAssignmentTypeInferer(object):
     # TODO: Implement a real type inference algorithm.
     # (Something more powerful than just extending this one...)
     def infer_types(self, scope):
-        enabled = not scope.is_closure_scope and scope.directives['infer_types']
+        closure_or_inner = scope.is_closure_scope or (scope.outer_scope and scope.outer_scope.is_closure_scope)
+        enabled = not closure_or_inner and scope.directives['infer_types']
         verbose = scope.directives['infer_types.verbose']
         if enabled == True:
             spanning_type = aggressive_spanning_type
diff --git a/tests/run/nonlocal_T490.pyx b/tests/run/nonlocal_T490.pyx
new file mode 100644 (file)
index 0000000..cd2d26b
--- /dev/null
@@ -0,0 +1,66 @@
+def simple():
+    """
+    >>> simple()
+    1
+    2
+    """
+    x = 1
+    y = 2
+    def f():
+        nonlocal x
+        nonlocal x, y
+        print(x)
+        print(y)
+    f()
+
+def assign():
+    """
+    >>> assign()
+    1
+    """
+    xx = 0
+    def ff():
+        nonlocal xx
+        xx += 1
+        print(xx)
+    ff()
+
+def nested():
+    """
+    >>> nested()
+    1
+    """
+    x = 0
+    def fx():
+        def gx():
+            nonlocal x
+            x=1
+            print(x)
+        return gx
+    fx()()
+
+def arg(x):
+    """
+    >>> arg('x')
+    xyy
+    """
+    def appendy():
+        nonlocal x
+        x += 'y'
+    x+='y'
+    appendy()
+    print x
+    return
+
+def argtype(int n):
+    """
+    >>> argtype(0)
+    1
+    """
+    def inc():
+        nonlocal n
+        n += 1
+    inc()
+    print n
+    return
+