From: Stefan Behnel Date: Thu, 16 Dec 2010 23:19:19 +0000 (+0100) Subject: manual merge of Haoyu's nonlocal implementation X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=d8813f05292b70bbb36855de845a8ed2cc160b43;p=cython.git manual merge of Haoyu's nonlocal implementation --- diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 39539cad..5ab4e598 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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. # diff --git a/Cython/Compiler/Parsing.pxd b/Cython/Compiler/Parsing.pxd index 3c646652..5d479b2a 100644 --- a/Cython/Compiler/Parsing.pxd +++ b/Cython/Compiler/Parsing.pxd @@ -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) diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index a98758a8..deefa5ba 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -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': diff --git a/Cython/Compiler/Scanning.py b/Cython/Compiler/Scanning.py index 232e82ea..d9f2586e 100644 --- a/Cython/Compiler/Scanning.py +++ b/Cython/Compiler/Scanning.py @@ -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", diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 068e99e1..5bbad507 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -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. diff --git a/Cython/Compiler/TypeInference.py b/Cython/Compiler/TypeInference.py index 0cf100ad..e50f7648 100644 --- a/Cython/Compiler/TypeInference.py +++ b/Cython/Compiler/TypeInference.py @@ -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 index 00000000..cd2d26b7 --- /dev/null +++ b/tests/run/nonlocal_T490.pyx @@ -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 +