[Python-checkins] python/dist/src/Python symtable.c,2.10.8.3,2.10.8.4
jhylton@users.sourceforge.net
jhylton@users.sourceforge.net
Sun, 04 Aug 2002 14:12:26 -0700
Update of /cvsroot/python/python/dist/src/Python
In directory usw-pr-cvs1:/tmp/cvs-serv21555/Python
Modified Files:
Tag: ast-branch
symtable.c
Log Message:
Implement analysis of scope information for PySymtable_Analyze().
The results are accessible from PyST_GetScope().
A comment explains the details of the analysis; it seems much cleaner
than the old implementation in compile.c, which tried to do everything
in a single pass.
The analysis is incomplete because it (probably) does not handle the
case where a free variable is bound in a scope that declares the name
global.
Index: symtable.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/symtable.c,v
retrieving revision 2.10.8.3
retrieving revision 2.10.8.4
diff -C2 -d -r2.10.8.3 -r2.10.8.4
*** symtable.c 9 Jul 2002 13:22:01 -0000 2.10.8.3
--- symtable.c 4 Aug 2002 21:12:15 -0000 2.10.8.4
***************
*** 6,14 ****
#include "structmember.h"
! PySymtableEntryObject *
! PySymtableEntry_New(struct symtable *st, identifier name, scope_ty scope,
! void *key, int lineno)
{
! PySymtableEntryObject *ste = NULL;
PyObject *k, *v;
--- 6,14 ----
#include "structmember.h"
! PySTEntryObject *
! PySTEntry_New(struct symtable *st, identifier name, block_ty block,
! void *key, int lineno)
{
! PySTEntryObject *ste = NULL;
PyObject *k, *v;
***************
*** 19,30 ****
v = PyDict_GetItem(st->st_symbols, k);
if (v) {
! assert(PySymtableEntry_Check(v));
Py_DECREF(k);
Py_INCREF(v);
! return (PySymtableEntryObject *)v;
}
! ste = (PySymtableEntryObject *)PyObject_New(PySymtableEntryObject,
! &PySymtableEntry_Type);
ste->ste_table = st;
ste->ste_id = k;
--- 19,30 ----
v = PyDict_GetItem(st->st_symbols, k);
if (v) {
! assert(PySTEntry_Check(v));
Py_DECREF(k);
Py_INCREF(v);
! return (PySTEntryObject *)v;
}
! ste = (PySTEntryObject *)PyObject_New(PySTEntryObject,
! &PySTEntry_Type);
ste->ste_table = st;
ste->ste_id = k;
***************
*** 48,52 ****
ste->ste_children = v;
! ste->ste_type = scope;
ste->ste_optimized = 0;
ste->ste_opt_lineno = 0;
--- 48,52 ----
ste->ste_children = v;
! ste->ste_type = block;
ste->ste_optimized = 0;
ste->ste_opt_lineno = 0;
***************
*** 56,60 ****
ste->ste_nested = 0;
else if (st->st_cur->ste_nested
! || st->st_cur->ste_type == FunctionScope)
ste->ste_nested = 1;
else
--- 56,60 ----
ste->ste_nested = 0;
else if (st->st_cur->ste_nested
! || st->st_cur->ste_type == FunctionBlock)
ste->ste_nested = 1;
else
***************
*** 73,77 ****
static PyObject *
! ste_repr(PySymtableEntryObject *ste)
{
char buf[256];
--- 73,77 ----
static PyObject *
! ste_repr(PySTEntryObject *ste)
{
char buf[256];
***************
*** 80,90 ****
"<symtable entry %.100s(%ld), line %d>",
PyString_AS_STRING(ste->ste_name),
! PyInt_AS_LONG(ste->ste_id),
! ste->ste_lineno);
return PyString_FromString(buf);
}
static void
! ste_dealloc(PySymtableEntryObject *ste)
{
ste->ste_table = NULL;
--- 80,89 ----
"<symtable entry %.100s(%ld), line %d>",
PyString_AS_STRING(ste->ste_name),
! PyInt_AS_LONG(ste->ste_id), ste->ste_lineno);
return PyString_FromString(buf);
}
static void
! ste_dealloc(PySTEntryObject *ste)
{
ste->ste_table = NULL;
***************
*** 97,101 ****
}
! #define OFF(x) offsetof(PySymtableEntryObject, x)
static PyMemberDef ste_memberlist[] = {
--- 96,100 ----
}
! #define OFF(x) offsetof(PySTEntryObject, x)
static PyMemberDef ste_memberlist[] = {
***************
*** 112,120 ****
};
! PyTypeObject PySymtableEntry_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"symtable entry",
! sizeof(PySymtableEntryObject),
0,
(destructor)ste_dealloc, /* tp_dealloc */
--- 111,119 ----
};
! PyTypeObject PySTEntry_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"symtable entry",
! sizeof(PySTEntryObject),
0,
(destructor)ste_dealloc, /* tp_dealloc */
***************
*** 154,160 ****
};
! static int symtable_enter_scope(struct symtable *st, identifier name,
! scope_ty scope, void *ast, int lineno);
! static int symtable_exit_scope(struct symtable *st, void *ast);
static int symtable_visit_stmt(struct symtable *st, stmt_ty s);
static int symtable_visit_expr(struct symtable *st, expr_ty s);
--- 153,159 ----
};
! static int symtable_enter_block(struct symtable *st, identifier name,
! block_ty block, void *ast, int lineno);
! static int symtable_exit_block(struct symtable *st, void *ast);
static int symtable_visit_stmt(struct symtable *st, stmt_ty s);
static int symtable_visit_expr(struct symtable *st, expr_ty s);
***************
*** 216,220 ****
st->st_filename = filename;
st->st_future = future;
! symtable_enter_scope(st, GET_IDENTIFIER(top), ModuleScope,
(void *)mod, 0);
/* Any other top-level initialization? */
--- 215,219 ----
st->st_filename = filename;
st->st_future = future;
! symtable_enter_block(st, GET_IDENTIFIER(top), ModuleBlock,
(void *)mod, 0);
/* Any other top-level initialization? */
***************
*** 228,251 ****
}
}
! symtable_exit_scope(st, (void *)mod);
return st;
}
! /* symtable_enter_scope() gets a reference via PySymtableEntry_New().
! This reference is released when the scope is exited, via the DECREF
! in symtable_exit_scope().
*/
static int
! symtable_exit_scope(struct symtable *st, void *ast)
{
int end;
- if (st->st_pass == 1)
- symtable_update_free_vars(st);
Py_DECREF(st->st_cur);
end = PyList_GET_SIZE(st->st_stack) - 1;
! st->st_cur = (PySymtableEntryObject *)PyList_GET_ITEM(st->st_stack,
end);
if (PySequence_DelItem(st->st_stack, end) < 0)
--- 227,479 ----
}
}
! symtable_exit_block(st, (void *)mod);
return st;
}
+ int
+ PySTEntry_GetScope(PySTEntryObject *ste, PyObject *name)
+ {
+ PyObject *v;
+ int flags;
! v = PyDict_GetItem(ste->ste_symbols, name);
! if (!v)
! return 0;
! assert(PyInt_Check(v));
! flags = PyInt_AS_LONG(v);
! flags = (flags >> SCOPE_OFF) & SCOPE_MASK;
! return flags;
! }
!
!
! /* Analyze raw symbol information to determine scope of each name.
!
! The next several functions are helpers for PySymtable_Analyze(),
! which determines whether a name is local, global, or free. In addition,
! it determines which local variables are cell variables; they provide
! bindings that are used for free variables in enclosed blocks.
!
! There are also two kinds of free variables, implicit and explicit. An
! explicit global is declared with the global statement. An implicit
! global is a free variable for which the compiler has found no binding
! in an enclosing function scope. The implicit global is either a global
! or a builtin. Python's module and class blocks use the xxx_NAME opcodes
! to handle these names to implement slightly odd semantics. In such a
! block, the name is treated as global until it is assigned to; then it
! is treated as a local.
!
! 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
! these facts during a pass over the PySTEntryObjects created during pass 1.
!
! When a function is entered during the second pass, the parent passes
! the set of all name bindings visible to its children. These bindings
! are used to determine if the variable is free or an implicit global.
! After doing the local analysis, it analyzes each of its child blocks
! using an updated set of name bindings.
!
! The children update the free variable set. If a local variable is free
! in a child, the variable is marked as a cell. The current function must
! provide runtime storage for the variable that may outlive the function's
! frame. Cell variables are removed from the free set before the analyze
! function returns to its parent.
!
! The sets of bound and free variables are implemented as dictionaries
! mapping strings to None.
! */
!
! #define SET_SCOPE(DICT, NAME, I) { \
! PyObject *o = PyInt_FromLong(I); \
! if (!o) \
! return 0; \
! if (PyDict_SetItem((DICT), (NAME), o) < 0) \
! return 0; \
! }
!
! /* XXX handle this:
! def f(x):
! global y
! def g():
! return x + y
! so that y is a global
! */
!
! int
! analyze_name(PyObject *dict, PyObject *name, int flags, PyObject *bound,
! PyObject *local, PyObject *free)
! {
! if (flags & DEF_GLOBAL) {
! if (flags & DEF_PARAM)
! return 0; /* can't declare a parameter as global */
! SET_SCOPE(dict, name, GLOBAL_EXPLICIT);
! return 1;
! }
! if (flags & DEF_BOUND) {
! SET_SCOPE(dict, name, LOCAL);
! if (PyDict_SetItem(local, name, Py_None) < 0)
! return 0;
! return 1;
! }
! if (bound && PyDict_GetItem(bound, name)) {
! SET_SCOPE(dict, name, FREE);
! if (PyDict_SetItem(free, name, Py_None) < 0)
! return 0;
! return 1;
! }
! else {
! SET_SCOPE(dict, name, GLOBAL_IMPLICIT);
! return 1;
! }
! return 0; /* Can't get here */
! }
!
! #undef SET_SCOPE
!
! /* If a name is defined in free and also in locals, then this block
! provides the binding for the free variable. The name should be
! marked CELL in this block and removed from the free list.
!
! Note that the current block's free variables are included in free.
! That's safe because no name can be free and local in the same scope.
! */
!
! int
! analyze_cells(PyObject *scope, PyObject *free)
! {
! PyObject *name, *v, *w;
! int flags, pos = 0, success = 0;
!
! w = PyInt_FromLong(CELL);
! if (!w)
! return 0;
! while (PyDict_Next(scope, &pos, &name, &v)) {
! assert(PyInt_Check(v));
! flags = PyInt_AS_LONG(v);
! if (flags != LOCAL)
! continue;
! if (!PyDict_GetItem(free, name))
! continue;
! /* Replace LOCAL with CELL for this name, and remove
! from free. It is safe to replace the value of name
! in the dict, because it will not cause a resize.
! */
! if (PyDict_SetItem(scope, name, w) < 0)
! goto error;
! if (!PyDict_DelItem(free, name) < 0)
! goto error;
! }
! success = 1;
! error:
! Py_DECREF(w);
! return success;
! }
!
! /* Enter the final scope information into the st_symbols dict. */
! int
! update_symbols(PyObject *symbols, PyObject *scope)
! {
! PyObject *name, *v, *u, *w;
! int i, flags, pos = 0;
!
! while (PyDict_Next(symbols, &pos, &name, &v)) {
! assert(PyInt_Check(v));
! flags = PyInt_AS_LONG(v);
! w = PyDict_GetItem(scope, name);
! assert(w && PyInt_Check(w));
! i = PyInt_AS_LONG(w);
! flags |= (i << SCOPE_OFF);
! u = PyInt_FromLong(flags);
! if (!PyDict_SetItem(symbols, name, u)) {
! Py_DECREF(u);
! return 0;
! }
! Py_DECREF(u);
! }
! return 1;
! }
!
! int
! analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free)
! {
! PyObject *name, *v, *local = NULL, *scope = NULL, *newbound = NULL;
! int i, flags, pos = 0, success = 0;
!
! local = PyDict_New();
! if (!local)
! goto error;
! scope = PyDict_New();
! if (!scope)
! goto error;
!
! while (PyDict_Next(ste->ste_symbols, &pos, &name, &v)) {
! flags = PyInt_AS_LONG(v);
! if (!analyze_name(scope, name, flags, bound, local, free))
! goto error;
! }
!
! /* create a new bound dictionary to pass to children */
! newbound = PyDict_New();
! if (!newbound)
! goto error;
! if (ste->ste_type != ClassBlock) {
! if (PyDict_Update(newbound, local) < 0)
! goto error;
! }
! if (bound) {
! if (PyDict_Update(newbound, bound) < 0)
! goto error;
! }
!
! /* call analyze_block() on each child */
! for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) {
! PyObject *c;
! c = PyDict_GetItem(ste->ste_table->st_symbols,
! PyList_GET_ITEM(ste->ste_children, i));
! assert(c && PySTEntry_Check(c));
! if (!analyze_block((PySTEntryObject *)c, local, free))
! goto error;
! }
!
! if (!analyze_cells(scope, free))
! goto error;
! if (!update_symbols(ste->ste_symbols, scope))
! goto error;
! success = 1;
! error:
! Py_XDECREF(local);
! Py_XDECREF(scope);
! Py_XDECREF(newbound);
! return success;
! }
!
! int
! PySymtable_Analyze(struct symtable *st)
! {
! PyObject *free;
! int r;
!
! free = PyDict_New();
! if (!free)
! return 0;
! r = analyze_block((PySTEntryObject *)st->st_global, NULL, free);
! Py_DECREF(free);
! return r;
! }
!
!
! /* symtable_enter_block() gets a reference via PySTEntry_New().
! This reference is released when the block is exited, via the DECREF
! in symtable_exit_block().
*/
static int
! symtable_exit_block(struct symtable *st, void *ast)
{
int end;
Py_DECREF(st->st_cur);
end = PyList_GET_SIZE(st->st_stack) - 1;
! st->st_cur = (PySTEntryObject *)PyList_GET_ITEM(st->st_stack,
end);
if (PySequence_DelItem(st->st_stack, end) < 0)
***************
*** 255,262 ****
static int
! symtable_enter_scope(struct symtable *st, identifier name, scope_ty scope,
void *ast, int lineno)
{
! PySymtableEntryObject *prev = NULL;
if (st->st_cur) {
--- 483,490 ----
static int
! symtable_enter_block(struct symtable *st, identifier name, block_ty block,
void *ast, int lineno)
{
! PySTEntryObject *prev = NULL;
if (st->st_cur) {
***************
*** 268,272 ****
}
}
! st->st_cur = PySymtableEntry_New(st, name, scope, ast, lineno);
if (name == GET_IDENTIFIER(top))
st->st_global = st->st_cur->ste_symbols;
--- 496,500 ----
}
}
! st->st_cur = PySTEntry_New(st, name, block, ast, lineno);
if (name == GET_IDENTIFIER(top))
st->st_global = st->st_cur->ste_symbols;
***************
*** 357,373 ****
if (s->v.FunctionDef.args->defaults)
VISIT_SEQ(st, expr, s->v.FunctionDef.args->defaults);
! symtable_enter_scope(st, s->v.FunctionDef.name, FunctionScope,
(void *)s, s->lineno);
VISIT(st, arguments, s->v.FunctionDef.args);
VISIT_SEQ(st, stmt, s->v.FunctionDef.body);
! symtable_exit_scope(st, s);
break;
case ClassDef_kind:
symtable_add_def(st, s->v.ClassDef.name, DEF_LOCAL);
VISIT_SEQ(st, expr, s->v.ClassDef.bases);
! symtable_enter_scope(st, s->v.ClassDef.name, ClassScope,
(void *)s, s->lineno);
VISIT_SEQ(st, stmt, s->v.ClassDef.body);
! symtable_exit_scope(st, s);
break;
case Return_kind:
--- 585,601 ----
if (s->v.FunctionDef.args->defaults)
VISIT_SEQ(st, expr, s->v.FunctionDef.args->defaults);
! symtable_enter_block(st, s->v.FunctionDef.name, FunctionBlock,
(void *)s, s->lineno);
VISIT(st, arguments, s->v.FunctionDef.args);
VISIT_SEQ(st, stmt, s->v.FunctionDef.body);
! symtable_exit_block(st, s);
break;
case ClassDef_kind:
symtable_add_def(st, s->v.ClassDef.name, DEF_LOCAL);
VISIT_SEQ(st, expr, s->v.ClassDef.bases);
! symtable_enter_block(st, s->v.ClassDef.name, ClassBlock,
(void *)s, s->lineno);
VISIT_SEQ(st, stmt, s->v.ClassDef.body);
! symtable_exit_block(st, s);
break;
case Return_kind:
***************
*** 490,497 ****
VISIT(st, arguments, e->v.Lambda.args);
/* XXX how to get line numbers for expressions */
! symtable_enter_scope(st, GET_IDENTIFIER(lambda),
! FunctionScope, (void *)e, 0);
VISIT(st, expr, e->v.Lambda.body);
! symtable_exit_scope(st, (void *)e);
break;
case Dict_kind:
--- 718,725 ----
VISIT(st, arguments, e->v.Lambda.args);
/* XXX how to get line numbers for expressions */
! symtable_enter_block(st, GET_IDENTIFIER(lambda),
! FunctionBlock, (void *)e, 0);
VISIT(st, expr, e->v.Lambda.body);
! symtable_exit_block(st, (void *)e);
break;
case Dict_kind:
***************
*** 591,595 ****
symtable_visit_arguments(struct symtable *st, arguments_ty a)
{
! /* skip default arguments inside function scope
XXX should ast be different?
*/
--- 819,823 ----
symtable_visit_arguments(struct symtable *st, arguments_ty a)
{
! /* skip default arguments inside function block
XXX should ast be different?
*/