mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
Provisional implementation of PEP 3104.
Add nonlocal_stmt to Grammar and Nonlocal node to AST. They both parallel the definitions for globals. The symbol table treats variables declared as nonlocal just like variables that are free implicitly. This change is missing the language spec changes, but makes some decisions about what the spec should say via the unittests. The PEP is silent on a number of decisions, so we should review those before claiming that nonlocal is complete. Thomas Wouters made the grammer and ast changes. Jeremy Hylton added the symbol table changes and the tests. Pete Shinners and Neal Norwitz helped review the code.
This commit is contained in:
parent
8b41c3dc28
commit
81e9502df6
12 changed files with 1188 additions and 922 deletions
|
@ -131,6 +131,10 @@ static PyTypeObject *Global_type;
|
|||
static char *Global_fields[]={
|
||||
"names",
|
||||
};
|
||||
static PyTypeObject *Nonlocal_type;
|
||||
static char *Nonlocal_fields[]={
|
||||
"names",
|
||||
};
|
||||
static PyTypeObject *Expr_type;
|
||||
static char *Expr_fields[]={
|
||||
"value",
|
||||
|
@ -507,6 +511,8 @@ static int init_types(void)
|
|||
if (!ImportFrom_type) return 0;
|
||||
Global_type = make_type("Global", stmt_type, Global_fields, 1);
|
||||
if (!Global_type) return 0;
|
||||
Nonlocal_type = make_type("Nonlocal", stmt_type, Nonlocal_fields, 1);
|
||||
if (!Nonlocal_type) return 0;
|
||||
Expr_type = make_type("Expr", stmt_type, Expr_fields, 1);
|
||||
if (!Expr_type) return 0;
|
||||
Pass_type = make_type("Pass", stmt_type, NULL, 0);
|
||||
|
@ -1145,6 +1151,20 @@ Global(asdl_seq * names, int lineno, int col_offset, PyArena *arena)
|
|||
return p;
|
||||
}
|
||||
|
||||
stmt_ty
|
||||
Nonlocal(asdl_seq * names, int lineno, int col_offset, PyArena *arena)
|
||||
{
|
||||
stmt_ty p;
|
||||
p = (stmt_ty)PyArena_Malloc(arena, sizeof(*p));
|
||||
if (!p)
|
||||
return NULL;
|
||||
p->kind = Nonlocal_kind;
|
||||
p->v.Nonlocal.names = names;
|
||||
p->lineno = lineno;
|
||||
p->col_offset = col_offset;
|
||||
return p;
|
||||
}
|
||||
|
||||
stmt_ty
|
||||
Expr(expr_ty value, int lineno, int col_offset, PyArena *arena)
|
||||
{
|
||||
|
@ -2197,6 +2217,15 @@ ast2obj_stmt(void* _o)
|
|||
goto failed;
|
||||
Py_DECREF(value);
|
||||
break;
|
||||
case Nonlocal_kind:
|
||||
result = PyType_GenericNew(Nonlocal_type, NULL, NULL);
|
||||
if (!result) goto failed;
|
||||
value = ast2obj_list(o->v.Nonlocal.names, ast2obj_identifier);
|
||||
if (!value) goto failed;
|
||||
if (PyObject_SetAttrString(result, "names", value) == -1)
|
||||
goto failed;
|
||||
Py_DECREF(value);
|
||||
break;
|
||||
case Expr_kind:
|
||||
result = PyType_GenericNew(Expr_type, NULL, NULL);
|
||||
if (!result) goto failed;
|
||||
|
@ -3049,6 +3078,8 @@ init_ast(void)
|
|||
0) return;
|
||||
if (PyDict_SetItemString(d, "Global", (PyObject*)Global_type) < 0)
|
||||
return;
|
||||
if (PyDict_SetItemString(d, "Nonlocal", (PyObject*)Nonlocal_type) < 0)
|
||||
return;
|
||||
if (PyDict_SetItemString(d, "Expr", (PyObject*)Expr_type) < 0) return;
|
||||
if (PyDict_SetItemString(d, "Pass", (PyObject*)Pass_type) < 0) return;
|
||||
if (PyDict_SetItemString(d, "Break", (PyObject*)Break_type) < 0) return;
|
||||
|
|
27
Python/ast.c
27
Python/ast.c
|
@ -2556,6 +2556,27 @@ ast_for_global_stmt(struct compiling *c, const node *n)
|
|||
return Global(s, LINENO(n), n->n_col_offset, c->c_arena);
|
||||
}
|
||||
|
||||
static stmt_ty
|
||||
ast_for_nonlocal_stmt(struct compiling *c, const node *n)
|
||||
{
|
||||
/* nonlocal_stmt: 'nonlocal' NAME (',' NAME)* */
|
||||
identifier name;
|
||||
asdl_seq *s;
|
||||
int i;
|
||||
|
||||
REQ(n, nonlocal_stmt);
|
||||
s = asdl_seq_new(NCH(n) / 2, c->c_arena);
|
||||
if (!s)
|
||||
return NULL;
|
||||
for (i = 1; i < NCH(n); i += 2) {
|
||||
name = NEW_IDENTIFIER(CHILD(n, i));
|
||||
if (!name)
|
||||
return NULL;
|
||||
asdl_seq_SET(s, i / 2, name);
|
||||
}
|
||||
return Nonlocal(s, LINENO(n), n->n_col_offset, c->c_arena);
|
||||
}
|
||||
|
||||
static stmt_ty
|
||||
ast_for_assert_stmt(struct compiling *c, const node *n)
|
||||
{
|
||||
|
@ -3063,8 +3084,8 @@ ast_for_stmt(struct compiling *c, const node *n)
|
|||
if (TYPE(n) == small_stmt) {
|
||||
REQ(n, small_stmt);
|
||||
n = CHILD(n, 0);
|
||||
/* small_stmt: expr_stmt | del_stmt | pass_stmt
|
||||
| flow_stmt | import_stmt | global_stmt | assert_stmt
|
||||
/* small_stmt: expr_stmt | del_stmt | pass_stmt | flow_stmt
|
||||
| import_stmt | global_stmt | nonlocal_stmt | assert_stmt
|
||||
*/
|
||||
switch (TYPE(n)) {
|
||||
case expr_stmt:
|
||||
|
@ -3079,6 +3100,8 @@ ast_for_stmt(struct compiling *c, const node *n)
|
|||
return ast_for_import_stmt(c, n);
|
||||
case global_stmt:
|
||||
return ast_for_global_stmt(c, n);
|
||||
case nonlocal_stmt:
|
||||
return ast_for_nonlocal_stmt(c, n);
|
||||
case assert_stmt:
|
||||
return ast_for_assert_stmt(c, n);
|
||||
default:
|
||||
|
|
|
@ -2239,6 +2239,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
|
|||
case ImportFrom_kind:
|
||||
return compiler_from_import(c, s);
|
||||
case Global_kind:
|
||||
case Nonlocal_kind:
|
||||
break;
|
||||
case Expr_kind:
|
||||
if (c->c_interactive && c->c_nestlevel <= 1) {
|
||||
|
|
1714
Python/graminit.c
1714
Python/graminit.c
File diff suppressed because it is too large
Load diff
|
@ -8,9 +8,15 @@
|
|||
#define GLOBAL_AFTER_ASSIGN \
|
||||
"name '%.400s' is assigned to before global declaration"
|
||||
|
||||
#define NONLOCAL_AFTER_ASSIGN \
|
||||
"name '%.400s' is assigned to before nonlocal declaration"
|
||||
|
||||
#define GLOBAL_AFTER_USE \
|
||||
"name '%.400s' is used prior to global declaration"
|
||||
|
||||
#define NONLOCAL_AFTER_USE \
|
||||
"name '%.400s' is used prior to nonlocal declaration"
|
||||
|
||||
#define IMPORT_STAR_WARNING "import * only allowed at module level"
|
||||
|
||||
#define RETURN_VAL_IN_GENERATOR \
|
||||
|
@ -328,6 +334,8 @@ PyST_GetScope(PySTEntryObject *ste, PyObject *name)
|
|||
block, the name is treated as global until it is assigned to; then it
|
||||
is treated as a local.
|
||||
|
||||
TODO(jhylton): Discuss nonlocal
|
||||
|
||||
The symbol table requires two passes to determine the scope of each name.
|
||||
The first pass collects raw facts from the AST: the name is a parameter
|
||||
here, the name is used by not defined here, etc. The second pass analyzes
|
||||
|
@ -378,6 +386,12 @@ analyze_name(PySTEntryObject *ste, PyObject *dict, PyObject *name, long flags,
|
|||
PyString_AS_STRING(name));
|
||||
return 0;
|
||||
}
|
||||
if (flags & DEF_NONLOCAL) {
|
||||
PyErr_Format(PyExc_SyntaxError,
|
||||
"name '%s' is nonlocal and global",
|
||||
PyString_AS_STRING(name));
|
||||
return 0;
|
||||
}
|
||||
SET_SCOPE(dict, name, GLOBAL_EXPLICIT);
|
||||
if (PyDict_SetItem(global, name, Py_None) < 0)
|
||||
return 0;
|
||||
|
@ -387,6 +401,24 @@ analyze_name(PySTEntryObject *ste, PyObject *dict, PyObject *name, long flags,
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
if (flags & DEF_NONLOCAL) {
|
||||
if (flags & DEF_PARAM) {
|
||||
PyErr_Format(PyExc_SyntaxError,
|
||||
"name '%s' is local and nonlocal",
|
||||
PyString_AS_STRING(name));
|
||||
return 0;
|
||||
}
|
||||
if (!PyDict_GetItem(bound, name)) {
|
||||
PyErr_Format(PyExc_SyntaxError,
|
||||
"no binding for nonlocal '%s' found",
|
||||
PyString_AS_STRING(name));
|
||||
|
||||
return 0;
|
||||
}
|
||||
SET_SCOPE(dict, name, FREE);
|
||||
ste->ste_free = 1;
|
||||
return PyDict_SetItem(free, name, Py_None) >= 0;
|
||||
}
|
||||
if (flags & DEF_BOUND) {
|
||||
SET_SCOPE(dict, name, LOCAL);
|
||||
if (PyDict_SetItem(local, name, Py_None) < 0)
|
||||
|
@ -405,24 +437,19 @@ analyze_name(PySTEntryObject *ste, PyObject *dict, PyObject *name, long flags,
|
|||
if (bound && PyDict_GetItem(bound, name)) {
|
||||
SET_SCOPE(dict, name, FREE);
|
||||
ste->ste_free = 1;
|
||||
if (PyDict_SetItem(free, name, Py_None) < 0)
|
||||
return 0;
|
||||
return 1;
|
||||
return PyDict_SetItem(free, name, Py_None) >= 0;
|
||||
}
|
||||
/* If a parent has a global statement, then call it global
|
||||
explicit? It could also be global implicit.
|
||||
*/
|
||||
else if (global && PyDict_GetItem(global, name)) {
|
||||
if (global && PyDict_GetItem(global, name)) {
|
||||
SET_SCOPE(dict, name, GLOBAL_EXPLICIT);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
if (ste->ste_nested)
|
||||
ste->ste_free = 1;
|
||||
SET_SCOPE(dict, name, GLOBAL_IMPLICIT);
|
||||
return 1;
|
||||
}
|
||||
return 0; /* Can't get here */
|
||||
if (ste->ste_nested)
|
||||
ste->ste_free = 1;
|
||||
SET_SCOPE(dict, name, GLOBAL_IMPLICIT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#undef SET_SCOPE
|
||||
|
@ -782,6 +809,7 @@ symtable_add_def(struct symtable *st, PyObject *name, int flag)
|
|||
long val;
|
||||
PyObject *mangled = _Py_Mangle(st->st_private, name);
|
||||
|
||||
|
||||
if (!mangled)
|
||||
return 0;
|
||||
dict = st->st_cur->ste_symbols;
|
||||
|
@ -1075,6 +1103,33 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case Nonlocal_kind: {
|
||||
int i;
|
||||
asdl_seq *seq = s->v.Nonlocal.names;
|
||||
for (i = 0; i < asdl_seq_LEN(seq); i++) {
|
||||
identifier name = (identifier)asdl_seq_GET(seq, i);
|
||||
char *c_name = PyString_AS_STRING(name);
|
||||
long cur = symtable_lookup(st, name);
|
||||
if (cur < 0)
|
||||
return 0;
|
||||
if (cur & (DEF_LOCAL | USE)) {
|
||||
char buf[256];
|
||||
if (cur & DEF_LOCAL)
|
||||
PyOS_snprintf(buf, sizeof(buf),
|
||||
NONLOCAL_AFTER_ASSIGN,
|
||||
c_name);
|
||||
else
|
||||
PyOS_snprintf(buf, sizeof(buf),
|
||||
NONLOCAL_AFTER_USE,
|
||||
c_name);
|
||||
if (!symtable_warn(st, buf, s->lineno))
|
||||
return 0;
|
||||
}
|
||||
if (!symtable_add_def(st, name, DEF_NONLOCAL))
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Expr_kind:
|
||||
VISIT(st, expr, s->v.Expr.value);
|
||||
break;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue