]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
This patch introduces an extra pass to the compiler that generates a
authorJeremy Hylton <jeremy@alum.mit.edu>
Fri, 19 Jan 2001 03:21:30 +0000 (03:21 +0000)
committerJeremy Hylton <jeremy@alum.mit.edu>
Fri, 19 Jan 2001 03:21:30 +0000 (03:21 +0000)
symbol table for each top-level compilation unit.  The information in
the symbol table allows the elimination of the later optimize() pass;
the bytecode generation emits the correct opcodes.

The current version passes the complete regression test, but may still
contain some bugs.  It's a fairly substantial revision.  The current
code adds an assert() and a test that may lead to a Py_FatalError().
I expect to remove these before 2.1 beta 1.

The symbol table (struct symtable) is described in comments in the
code.

The changes affects the several com_XXX() functions that were used to
emit LOAD_NAME and its ilk.  The primary interface for this bytecode
is now com_addop_varname() which takes a kind and a name, where kind
is one of VAR_LOAD, VAR_STORE, or VAR_DELETE.

There are many other smaller changes:

- The name mangling code is no longer contained in ifdefs.  There are
  two functions that expose the mangling logical: com_mangle() and
  symtable_mangle().

- The com_error() function can accept NULL for its first argument;
  this is useful with is_constant_false() is called during symbol
  table generation.

- The loop index names used by list comprehensions have been changed
  from __1__ to [1], so that they can not be accessed by Python code.

- in com_funcdef(), com_argdefs() is now called before the body of the
  function is compiled.  This provides consistency with com_lambdef()
  and symtable_funcdef().

- Helpers do_pad(), dump(), and DUMP() are added to aid in debugging
  the compiler.

Python/compile.c

index 734bfcf5aa3a2870fc3de0b1ae1e7ff042e6fa34..8e1cfd83ae75ddec21bd23319bacce74444c1ad4 100644 (file)
    XXX other JAR tricks?
 */
 
-#ifndef NO_PRIVATE_NAME_MANGLING
-#define PRIVATE_NAME_MANGLING
-#endif
-
 #include "Python.h"
 
 #include "node.h"
@@ -45,6 +41,23 @@ int Py_OptimizeFlag = 0;
 #define OP_ASSIGN 1
 #define OP_APPLY 2
 
+#define VAR_LOAD 0
+#define VAR_STORE 1
+#define VAR_DELETE 2
+
+#define NAME_LOCAL 0
+#define NAME_GLOBAL 1
+#define NAME_DEFAULT 2
+
+#define TYPE_FUNCTION 0
+#define TYPE_CLASS 1
+#define TYPE_MODULE 2
+
+#define IMPLICIT_GLOBAL 1
+#define EXPLICIT_GLOBAL 2
+
+#define MANGLE_LEN 256
+
 #define OFF(x) offsetof(PyCodeObject, x)
 
 static struct memberlist code_memberlist[] = {
@@ -268,6 +281,19 @@ PyCode_New(int argcount, int nlocals, int stacksize, int flags,
 
 /* Data structure used internally */
 
+/* The compiler uses two passes to generate bytecodes.  The first pass
+   builds the symbol table.  The second pass generates the bytecode.
+
+   The first pass uses a single symtable struct.  The second pass uses
+   a compiling struct for each code block.  The compiling structs
+   share a reference to the symtable.
+
+   The two passes communicate via symtable_load_symbols() and via
+   is_local() and is_global().  The former initializes several slots
+   in the compiling struct: c_varnames, c_locals, c_nlocals,
+   c_argcount, c_globals, and c_flags.
+*/
+
 struct compiling {
        PyObject *c_code;       /* string */
        PyObject *c_consts;     /* list of objects */
@@ -296,12 +322,65 @@ struct compiling {
        int c_firstlineno;
        PyObject *c_lnotab;     /* Table mapping address to line number */
        int c_last_addr, c_last_line, c_lnotab_next;
-#ifdef PRIVATE_NAME_MANGLING
        char *c_private;        /* for private name mangling */
-#endif
        int c_tmpname;          /* temporary local name counter */
+       struct symtable *c_symtable;   /* pointer to module symbol table */
+};
+
+/* The symbol table is constructed each time PyNode_Compile() is
+   called.  The table walks the entire parse tree and identifies each
+   use or definition of a variable. 
+
+   The symbol table contains a dictionary for each code block in a
+   module: The symbol dictionary for the block.  They keys of these
+   dictionaries are the name of all variables used or defined in the
+   block; the integer values are used to store several flags,
+   e.g. DEF_PARAM indicates that a variable is a parameter to a
+   function. 
+
+   The slots st_cur, st_cur_id, and st_cur_type always refer to the
+   current code block.  The st_cur slot is the symbol dictionary.  The
+   st_cur_id slot is the id is the key in st_symbols.  The st_cur_type
+   slot is one of TYPE_FUNCTION, TYPE_CLASS, or TYPE_MODULE.
+
+   The st_symbols slot is a dictionary that maps code block ids to
+   symbol dictionaries.  The keys are generated by a counter that is
+   incremented each time a new code block is found.  The counter is
+   identifies a specific scope, because both passes walk the parse
+   tree in the same order.
+
+   The st_varnames slot is a dictionary that maps code block ids to
+   parameter lists.  The st_global slot always refers to the symbol 
+   dictionary for the module.
+*/
+
+struct symtable {
+       PyObject *st_symbols;    /* dictionary of symbol tables */
+       PyObject *st_varnames;   /* dictionary of parameter lists */
+       PyObject *st_namespaces; /* pointer to list of namespaces */
+       PyObject *st_types;      /* pointer to list of namespace types */
+       PyObject *st_cur;        /* borrowed ref to dict in st_symbols */
+       PyObject *st_cur_id;     /* int id of current code block */
+       int st_cur_type;         /* type of current scope */ 
+       PyObject *st_global;     /* borrowed ref to MODULE in st_symbols */
+       int st_scopes;           /* number of scopes */
+       int st_errors;           /* number of errors */
+       char *st_private;        /* name of current class or NULL */
+       int st_tmpname;          /* temporary name counter */
 };
 
+#define GLOBAL "global"
+#define NOOPT ".noopt"
+
+/* Flags for def-use information */
+
+#define DEF_GLOBAL 1
+#define DEF_LOCAL 2
+#define DEF_PARAM 2<<1
+#define USE 2<<2
+#define DEF_STAR 2<<3
+#define DEF_DOUBLESTAR 2<<4
+#define DEF_INTUPLE 2<<5
 
 /* Error message including line number */
 
@@ -309,6 +388,12 @@ static void
 com_error(struct compiling *c, PyObject *exc, char *msg)
 {
        PyObject *v, *tb, *tmp;
+       if (c == NULL) {
+               /* Error occurred via symtable call to
+                  is_constant_false */
+               PyErr_SetString(exc, msg);
+               return;
+       }
        c->c_errors++;
        if (c->c_lineno <= 1) {
                /* Unknown line number or single interactive command */
@@ -378,8 +463,8 @@ static void com_free(struct compiling *);
 static void com_push(struct compiling *, int);
 static void com_pop(struct compiling *, int);
 static void com_done(struct compiling *);
-static void com_node(struct compiling *, struct _node *);
-static void com_factor(struct compiling *, struct _node *);
+static void com_node(struct compiling *, node *);
+static void com_factor(struct compiling *, node *);
 static void com_addbyte(struct compiling *, int);
 static void com_addint(struct compiling *, int);
 static void com_addoparg(struct compiling *, int, int);
@@ -392,16 +477,63 @@ static void com_addopname(struct compiling *, int, node *);
 static void com_list(struct compiling *, node *, int);
 static void com_list_iter(struct compiling *, node *, node *, char *);
 static int com_argdefs(struct compiling *, node *);
-static int com_newlocal(struct compiling *, char *);
 static void com_assign(struct compiling *, node *, int, node *);
 static void com_assign_name(struct compiling *, node *, int);
-static PyCodeObject *icompile(struct _node *, struct compiling *);
-static PyCodeObject *jcompile(struct _node *, char *,
-                             struct compiling *);
+static PyCodeObject *icompile(node *, struct compiling *);
+static PyCodeObject *jcompile(node *, char *, struct compiling *);
 static PyObject *parsestrplus(node *);
 static PyObject *parsestr(char *);
 static node *get_rawdocstring(node *);
 
+static int is_local(struct compiling *, char *);
+static int is_global(struct compiling *, char *);
+
+/* symtable operations */
+static int symtable_build(struct compiling *, node *);
+static int symtable_load_symbols(struct compiling *);
+static struct symtable *symtable_init(void);
+static void symtable_free(struct symtable *);
+static int symtable_enter_scope(struct symtable *, char *, int);
+static int symtable_exit_scope(struct symtable *);
+static int symtable_update_cur(struct symtable *);
+static int symtable_add_def(struct symtable *, char *, int);
+static int symtable_add_use(struct symtable *, char *);
+
+static void symtable_node(struct symtable *, node *);
+static void symtable_funcdef(struct symtable *, node *);
+static void symtable_default_args(struct symtable *, node *);
+static void symtable_params(struct symtable *, node *);
+static void symtable_params_fplist(struct symtable *, node *n);
+static void symtable_global(struct symtable *, node *);
+static void symtable_import(struct symtable *, node *);
+static void symtable_assign(struct symtable *, node *);
+static void symtable_list_comprehension(struct symtable *, node *);
+
+/* helper */
+static void
+do_pad(int pad)
+{
+       int i;
+       for (i = 0; i < pad; ++i)
+               fprintf(stderr, "  ");
+}
+
+static void
+dump(node *n, int pad, int depth)
+{
+       int i;
+       if (depth == 0)
+           return;
+       do_pad(pad);
+       fprintf(stderr, "%d: %s\n", TYPE(n), STR(n));
+       if (depth > 0)
+           depth--;
+       for (i = 0; i < NCH(n); ++i)
+               dump(CHILD(n, i), pad + 1, depth);
+}
+
+#define DUMP(N) dump(N, 0, -1)
+
 static int
 com_init(struct compiling *c, char *filename)
 {
@@ -421,11 +553,10 @@ com_init(struct compiling *c, char *filename)
                goto fail;
        if ((c->c_locals = PyDict_New()) == NULL)
                goto fail;
-       if ((c->c_varnames = PyList_New(0)) == NULL)
-               goto fail;
        if ((c->c_lnotab = PyString_FromStringAndSize((char *)NULL,
                                                      1000)) == NULL)
                goto fail;
+       c->c_varnames = NULL;
        c->c_nlocals = 0;
        c->c_argcount = 0;
        c->c_flags = 0;
@@ -446,6 +577,7 @@ com_init(struct compiling *c, char *filename)
        c->c_last_line = 0;
        c-> c_lnotab_next = 0;
        c->c_tmpname = 0;
+       c->c_symtable = NULL;
        return 1;
        
   fail:
@@ -503,6 +635,7 @@ com_addbyte(struct compiling *c, int byte)
 {
        int len;
        /*fprintf(stderr, "%3d: %3d\n", c->c_nexti, byte);*/
+       assert(byte >= 0 && byte <= 255);
        if (byte < 0 || byte > 255) {
                /*
                fprintf(stderr, "XXX compiling bad byte: %d\n", byte);
@@ -640,7 +773,7 @@ com_add(struct compiling *c, PyObject *list, PyObject *dict, PyObject *v)
 {
        PyObject *w, *t, *np=NULL;
        long n;
-
+       
        t = Py_BuildValue("(OO)", v, v->ob_type);
        if (t == NULL)
            goto fail;
@@ -679,20 +812,19 @@ com_addname(struct compiling *c, PyObject *v)
        return com_add(c, c->c_names, c->c_name_dict, v);
 }
 
-#ifdef PRIVATE_NAME_MANGLING
 static int
-com_mangle(struct compiling *c, char *name, char *buffer, size_t maxlen)
+mangle(char *p, char *name, char *buffer, size_t maxlen)
 {
        /* Name mangling: __private becomes _classname__private.
           This is independent from how the name is used. */
-       char *p;
        size_t nlen, plen;
+       if (p == NULL || name == NULL || name[0] != '_' || name[1] != '_')
+               return 0;
        nlen = strlen(name);
        if (nlen+2 >= maxlen)
                return 0; /* Don't mangle __extremely_long_names */
        if (name[nlen-1] == '_' && name[nlen-2] == '_')
                return 0; /* Don't mangle __whatever__ */
-       p = c->c_private;
        /* Strip leading underscores from class name */
        while (*p == '_')
                p++;
@@ -708,20 +840,24 @@ com_mangle(struct compiling *c, char *name, char *buffer, size_t maxlen)
        /* fprintf(stderr, "mangle %s -> %s\n", name, buffer); */
        return 1;
 }
-#endif
+
+static int
+com_mangle(struct compiling *c, char *name, char *buffer, size_t maxlen)
+{
+       return mangle(c->c_private, name, buffer, maxlen);
+}
 
 static void
-com_addopnamestr(struct compiling *c, int op, char *name)
+com_addop_name(struct compiling *c, int op, char *name)
 {
        PyObject *v;
        int i;
-#ifdef PRIVATE_NAME_MANGLING
-       char buffer[256];
-       if (name != NULL && name[0] == '_' && name[1] == '_' &&
-           c->c_private != NULL &&
-           com_mangle(c, name, buffer, sizeof(buffer)))
+       char buffer[MANGLE_LEN];
+/*     fprintf(stderr, "com_addop_name(%s, %d, %s)\n",
+               c->c_name, op, name);
+*/
+       if (com_mangle(c, name, buffer, sizeof(buffer)))
                name = buffer;
-#endif
        if (name == NULL || (v = PyString_InternFromString(name)) == NULL) {
                c->c_errors++;
                i = 255;
@@ -730,19 +866,98 @@ com_addopnamestr(struct compiling *c, int op, char *name)
                i = com_addname(c, v);
                Py_DECREF(v);
        }
-       /* Hack to replace *_NAME opcodes by *_GLOBAL if necessary */
-       switch (op) {
-       case LOAD_NAME:
-       case STORE_NAME:
-       case DELETE_NAME:
-               if (PyDict_GetItemString(c->c_globals, name) != NULL) {
-                       switch (op) {
-                       case LOAD_NAME:   op = LOAD_GLOBAL;   break;
-                       case STORE_NAME:  op = STORE_GLOBAL;  break;
-                       case DELETE_NAME: op = DELETE_GLOBAL; break;
-                       }
+       com_addoparg(c, op, i);
+}
+
+static void
+com_addop_varname(struct compiling *c, int kind, char *name)
+{
+       PyObject *v;
+       int i;
+       int scope;
+       int op = STOP_CODE;
+       char buffer[MANGLE_LEN];
+
+       if (com_mangle(c, name, buffer, sizeof(buffer)))
+               name = buffer;
+       if (name == NULL || (v = PyString_InternFromString(name)) == NULL) {
+               c->c_errors++;
+               i = 255;
+               goto done;
+       }
+       scope = NAME_DEFAULT;
+       if (c->c_symtable->st_cur_type == TYPE_FUNCTION && is_local(c, name)) {
+               scope = NAME_LOCAL;
+       } else {
+               int g = is_global(c, name);
+               if ((g & EXPLICIT_GLOBAL) 
+                   || ((c->c_flags & CO_OPTIMIZED) && g))
+                       scope = NAME_GLOBAL;
+       }
+
+       /* XXX Test can probably be eliminated once we reach 2.1 beta 1 */
+       if (!(is_local(c, name) || is_global(c, name))) {
+               fprintf(stderr, "name: %s", name);
+               fprintf(stderr, ", in %s, file '%s', line %d\n",
+                       c->c_name, c->c_filename, c->c_lineno);
+               fprintf(stderr, "locals: %s\n", 
+                       PyString_AS_STRING(PyObject_Repr(c->c_locals)));
+               fprintf(stderr, "globals: %s\n", 
+                       PyString_AS_STRING(PyObject_Repr(c->c_globals)));
+               Py_FatalError("compiler did not label name as local or global");
+       }
+
+       i = com_addname(c, v);
+       if (scope == NAME_LOCAL) {
+               PyObject *w = PyDict_GetItem(c->c_locals, v);
+               if (w == NULL) {
+                       c->c_errors++;
+                       i = 255;
+                       goto done;
+               } else 
+                       i = PyInt_AsLong(w);
+       }
+       Py_DECREF(v);
+
+       switch (kind) {
+       case VAR_LOAD:
+               switch (scope) {
+               case NAME_LOCAL:
+                       op = LOAD_FAST;
+                       break;
+               case NAME_GLOBAL:
+                       op = LOAD_GLOBAL;
+                       break;
+               case NAME_DEFAULT:
+                       op = LOAD_NAME;
+               }
+               break;
+       case VAR_STORE:
+               switch (scope) {
+               case NAME_LOCAL:
+                       op = STORE_FAST;
+                       break;
+               case NAME_GLOBAL:
+                       op = STORE_GLOBAL;
+                       break;
+               case NAME_DEFAULT:
+                       op = STORE_NAME;
                }
+               break;
+       case VAR_DELETE:
+               switch (scope) {
+               case NAME_LOCAL:
+                       op = DELETE_FAST;
+                       break;
+               case NAME_GLOBAL:
+                       op = DELETE_GLOBAL;
+                       break;
+               case NAME_DEFAULT:
+                       op = DELETE_NAME;
+               }
+               break;
        }
+done:
        com_addoparg(c, op, i);
 }
 
@@ -778,7 +993,7 @@ com_addopname(struct compiling *c, int op, node *n)
                REQ(n, NAME);
                name = STR(n);
        }
-       com_addopnamestr(c, op, name);
+       com_addop_name(c, op, name);
 }
 
 static PyObject *
@@ -1062,7 +1277,7 @@ com_list_iter(struct compiling *c,
                }
        }
        else {
-               com_addopnamestr(c, LOAD_NAME, t);
+               com_addop_varname(c, VAR_LOAD, t);
                com_push(c, 1);
                com_node(c, e);
                com_addoparg(c, CALL_FUNCTION, 1);
@@ -1076,15 +1291,15 @@ com_list_comprehension(struct compiling *c, node *n)
 {
        /* listmaker: test list_for */
        char tmpname[12];
-       sprintf(tmpname, "__%d__", ++c->c_tmpname);
+       sprintf(tmpname, "[%d]", ++c->c_tmpname);
        com_addoparg(c, BUILD_LIST, 0);
        com_addbyte(c, DUP_TOP); /* leave the result on the stack */
        com_push(c, 2);
-       com_addopnamestr(c, LOAD_ATTR, "append");
-       com_addopnamestr(c, STORE_NAME, tmpname);
+       com_addop_name(c, LOAD_ATTR, "append");
+       com_addop_varname(c, VAR_STORE, tmpname);
        com_pop(c, 1);
        com_list_for(c, CHILD(n, 1), CHILD(n, 0), tmpname);
-       com_addopnamestr(c, DELETE_NAME, tmpname);
+       com_addop_varname(c, VAR_DELETE, tmpname);
        --c->c_tmpname;
 }
 
@@ -1182,7 +1397,7 @@ com_atom(struct compiling *c, node *n)
                com_push(c, 1);
                break;
        case NAME:
-               com_addopname(c, LOAD_NAME, ch);
+               com_addop_varname(c, VAR_LOAD, STR(ch));
                com_push(c, 1);
                break;
        default:
@@ -1871,7 +2086,9 @@ com_test(struct compiling *c, node *n)
                PyObject *v;
                int i;
                int ndefs = com_argdefs(c, CHILD(n, 0));
+               symtable_enter_scope(c->c_symtable, "lambda", lambdef);
                v = (PyObject *) icompile(CHILD(n, 0), c);
+               symtable_exit_scope(c->c_symtable);
                if (v == NULL) {
                        c->c_errors++;
                        i = 255;
@@ -1986,7 +2203,7 @@ static void
 com_augassign_name(struct compiling *c, node *n, int opcode, node *augn)
 {
        REQ(n, NAME);
-       com_addopname(c, LOAD_NAME, n);
+       com_addop_varname(c, VAR_LOAD, STR(n));
        com_push(c, 1);
        com_node(c, augn);
        com_addbyte(c, opcode);
@@ -1998,7 +2215,7 @@ static void
 com_assign_name(struct compiling *c, node *n, int assigning)
 {
        REQ(n, NAME);
-       com_addopname(c, assigning ? STORE_NAME : DELETE_NAME, n);
+       com_addop_varname(c, assigning ? VAR_STORE : VAR_DELETE, STR(n));
        if (assigning)
                com_pop(c, 1);
 }
@@ -2080,7 +2297,7 @@ com_assign(struct compiling *c, node *n, int assigning, node *augn)
                                }
                                if (assigning > OP_APPLY) {
                                        com_error(c, PyExc_SyntaxError,
-                                                 "augmented assign to tuple not possible");
+                                 "augmented assign to tuple not possible");
                                        return;
                                }
                                break;
@@ -2093,7 +2310,13 @@ com_assign(struct compiling *c, node *n, int assigning, node *augn)
                                }
                                if (assigning > OP_APPLY) {
                                        com_error(c, PyExc_SyntaxError,
-                                                 "augmented assign to list not possible");
+                                 "augmented assign to list not possible");
+                                       return;
+                               }
+                               if (NCH(n) > 1 
+                                   && TYPE(CHILD(n, 1)) == list_for) {
+                                       com_error(c, PyExc_SyntaxError,
+                                         "can't assign to list comprehension");
                                        return;
                                }
                                com_assign_sequence(c, n, assigning);
@@ -2201,9 +2424,14 @@ com_assert_stmt(struct compiling *c, node *n)
 
           where <message> is the second test, if present.
        */
+
+       /* XXX should __debug__ and AssertionError get inserted into
+          the symbol table?  they don't follow the normal rules
+          because they are always loaded as globals */
+
        if (Py_OptimizeFlag)
                return;
-       com_addopnamestr(c, LOAD_GLOBAL, "__debug__");
+       com_addop_name(c, LOAD_GLOBAL, "__debug__");
        com_push(c, 1);
        com_addfwref(c, JUMP_IF_FALSE, &a);
        com_addbyte(c, POP_TOP);
@@ -2213,7 +2441,7 @@ com_assert_stmt(struct compiling *c, node *n)
        com_addbyte(c, POP_TOP);
        com_pop(c, 1);
        /* Raise that exception! */
-       com_addopnamestr(c, LOAD_GLOBAL, "AssertionError");
+       com_addop_name(c, LOAD_GLOBAL, "AssertionError");
        com_push(c, 1);
        i = NCH(n)/2; /* Either 2 or 4 */
        if (i > 1)
@@ -2332,9 +2560,9 @@ com_from_import(struct compiling *c, node *n)
                        com_error(c, PyExc_SyntaxError, "invalid syntax");
                        return;
                }
-               com_addopname(c, STORE_NAME, CHILD(n, 2));
+               com_addop_varname(c, VAR_STORE, STR(CHILD(n, 2)));
        } else
-               com_addopname(c, STORE_NAME, CHILD(n, 0));
+               com_addop_varname(c, VAR_STORE, STR(CHILD(n, 0)));
        com_pop(c, 1);
 }
 
@@ -2390,87 +2618,19 @@ com_import_stmt(struct compiling *c, node *n)
                                }
                                for (j=2 ; j < NCH(CHILD(subn, 0)); j += 2)
                                        com_addopname(c, LOAD_ATTR,
-                                                     CHILD(CHILD(subn, 0), j));
-                               com_addopname(c, STORE_NAME, CHILD(subn, 2));
+                                                     CHILD(CHILD(subn, 0),
+                                                           j));
+                               com_addop_varname(c, VAR_STORE,
+                                                 STR(CHILD(subn, 2)));
                        } else
-                               com_addopname(c, STORE_NAME,
-                                             CHILD(CHILD(subn, 0),0));
+                               com_addop_varname(c, VAR_STORE,
+                                                 STR(CHILD(CHILD(subn, 0),
+                                                           0))); 
                        com_pop(c, 1);
                }
        }
 }
 
-static void
-com_global_stmt(struct compiling *c, node *n)
-{
-       int i;
-       REQ(n, global_stmt);
-       /* 'global' NAME (',' NAME)* */
-       for (i = 1; i < NCH(n); i += 2) {
-               char *s = STR(CHILD(n, i));
-#ifdef PRIVATE_NAME_MANGLING
-               char buffer[256];
-               if (s != NULL && s[0] == '_' && s[1] == '_' &&
-                   c->c_private != NULL &&
-                   com_mangle(c, s, buffer, sizeof(buffer)))
-                       s = buffer;
-#endif
-               if (PyDict_GetItemString(c->c_locals, s) != NULL) {
-                       com_error(c, PyExc_SyntaxError,
-                                 "name is local and global");
-               }
-               else if (PyDict_SetItemString(c->c_globals, s, Py_None) != 0)
-                       c->c_errors++;
-       }
-}
-
-static int
-com_newlocal_o(struct compiling *c, PyObject *nameval)
-{
-       int i;
-       PyObject *ival;
-       if (PyList_Size(c->c_varnames) != c->c_nlocals) {
-               /* This is usually caused by an error on a previous call */
-               if (c->c_errors == 0) {
-                       com_error(c, PyExc_SystemError,
-                                 "mixed up var name/index");
-               }
-               return 0;
-       }
-       ival = PyInt_FromLong(i = c->c_nlocals++);
-       if (ival == NULL)
-               c->c_errors++;
-       else if (PyDict_SetItem(c->c_locals, nameval, ival) != 0)
-               c->c_errors++;
-       else if (PyList_Append(c->c_varnames, nameval) != 0)
-               c->c_errors++;
-       Py_XDECREF(ival);
-       return i;
-}
-
-static int
-com_addlocal_o(struct compiling *c, PyObject *nameval)
-{
-       PyObject *ival =  PyDict_GetItem(c->c_locals, nameval);
-       if (ival != NULL)
-               return PyInt_AsLong(ival);
-       return com_newlocal_o(c, nameval);
-}
-
-static int
-com_newlocal(struct compiling *c, char *name)
-{
-       PyObject *nameval = PyString_InternFromString(name);
-       int i;
-       if (nameval == NULL) {
-               c->c_errors++;
-               return 0;
-       }
-       i = com_newlocal_o(c, nameval);
-       Py_DECREF(nameval);
-       return i;
-}
-
 static void
 com_exec_stmt(struct compiling *c, node *n)
 {
@@ -2498,6 +2658,7 @@ is_constant_false(struct compiling *c, node *n)
 {
        PyObject *v;
        int i;
+       /* argument c will be NULL when called from symtable_node() */
 
   /* Label to avoid tail recursion */
   next:
@@ -3031,18 +3192,21 @@ static void
 com_funcdef(struct compiling *c, node *n)
 {
        PyObject *v;
+       int ndefs;
        REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */
+       ndefs = com_argdefs(c, n);
+       symtable_enter_scope(c->c_symtable, STR(CHILD(n, 1)), TYPE(n));
        v = (PyObject *)icompile(n, c);
+       symtable_exit_scope(c->c_symtable);
        if (v == NULL)
                c->c_errors++;
        else {
                int i = com_addconst(c, v);
-               int ndefs = com_argdefs(c, n);
                com_addoparg(c, LOAD_CONST, i);
                com_push(c, 1);
                com_addoparg(c, MAKE_FUNCTION, ndefs);
                com_pop(c, ndefs);
-               com_addopname(c, STORE_NAME, CHILD(n, 1));
+               com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1)));
                com_pop(c, 1);
                Py_DECREF(v);
        }
@@ -3066,6 +3230,8 @@ com_classdef(struct compiling *c, node *n)
 {
        int i;
        PyObject *v;
+       char *name;
+
        REQ(n, classdef);
        /* classdef: class NAME ['(' testlist ')'] ':' suite */
        if ((v = PyString_InternFromString(STR(CHILD(n, 1)))) == NULL) {
@@ -3084,7 +3250,10 @@ com_classdef(struct compiling *c, node *n)
        }
        else
                com_bases(c, CHILD(n, 3));
+       name = STR(CHILD(n, 1));
+       symtable_enter_scope(c->c_symtable, name, TYPE(n));
        v = (PyObject *)icompile(n, c);
+       symtable_exit_scope(c->c_symtable);
        if (v == NULL)
                c->c_errors++;
        else {
@@ -3095,7 +3264,7 @@ com_classdef(struct compiling *c, node *n)
                com_addoparg(c, CALL_FUNCTION, 0);
                com_addbyte(c, BUILD_CLASS);
                com_pop(c, 2);
-               com_addopname(c, STORE_NAME, CHILD(n, 1));
+               com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1)));
                Py_DECREF(v);
        }
 }
@@ -3103,6 +3272,7 @@ com_classdef(struct compiling *c, node *n)
 static void
 com_node(struct compiling *c, node *n)
 {
+ loop:
        switch (TYPE(n)) {
        
        /* Definition nodes */
@@ -3119,8 +3289,8 @@ com_node(struct compiling *c, node *n)
        case stmt:
        case small_stmt:
        case flow_stmt:
-               com_node(c, CHILD(n, 0));
-               break;
+               n = CHILD(n, 0);
+               goto loop;
 
        case simple_stmt:
                /* small_stmt (';' small_stmt)* [';'] NEWLINE */
@@ -3134,8 +3304,8 @@ com_node(struct compiling *c, node *n)
        
        case compound_stmt:
                com_addoparg(c, SET_LINENO, n->n_lineno);
-               com_node(c, CHILD(n, 0));
-               break;
+               n = CHILD(n, 0);
+               goto loop;
 
        /* Statement nodes */
        
@@ -3170,7 +3340,6 @@ com_node(struct compiling *c, node *n)
                com_import_stmt(c, n);
                break;
        case global_stmt:
-               com_global_stmt(c, n);
                break;
        case exec_stmt:
                com_exec_stmt(c, n);
@@ -3258,7 +3427,7 @@ com_fpdef(struct compiling *c, node *n)
        if (TYPE(CHILD(n, 0)) == LPAR)
                com_fplist(c, CHILD(n, 1));
        else {
-               com_addoparg(c, STORE_FAST, com_newlocal(c, STR(CHILD(n, 0))));
+               com_addop_varname(c, VAR_STORE, STR(CHILD(n, 0)));
                com_pop(c, 1);
        }
 }
@@ -3279,6 +3448,10 @@ com_fplist(struct compiling *c, node *n)
        }
 }
 
+/* XXX This function could probably be made simpler, because it
+   doesn't do anything except generate code for complex arguments. 
+*/
+
 static void
 com_arglist(struct compiling *c, node *n)
 {
@@ -3294,7 +3467,6 @@ com_arglist(struct compiling *c, node *n)
                node *ch = CHILD(n, i);
                node *fp;
                char *name;
-               PyObject *nameval;
                if (TYPE(ch) == STAR || TYPE(ch) == DOUBLESTAR)
                        break;
                REQ(ch, fpdef); /* fpdef: NAME | '(' fplist ')' */
@@ -3306,17 +3478,7 @@ com_arglist(struct compiling *c, node *n)
                        sprintf(nbuf, ".%d", i);
                        complex = 1;
                }
-               nameval = PyString_InternFromString(name);
-               if (nameval == NULL) {
-                       c->c_errors++;
-               }
-               if (PyDict_GetItem(c->c_locals, nameval)) {
-                       com_error(c, PyExc_SyntaxError,
-                                 "duplicate argument in function definition");
-               }
-               com_newlocal_o(c, nameval);
-               Py_DECREF(nameval);
-               c->c_argcount++;
+               /* all name updates handled by symtable */
                if (++i >= nch)
                        break;
                ch = CHILD(n, i);
@@ -3333,9 +3495,7 @@ com_arglist(struct compiling *c, node *n)
                        REQ(ch, STAR);
                        ch = CHILD(n, i+1);
                        if (TYPE(ch) == NAME) {
-                               c->c_flags |= CO_VARARGS;
                                i += 3;
-                               com_newlocal(c, STR(ch));
                        }
                }
        }
@@ -3352,8 +3512,6 @@ com_arglist(struct compiling *c, node *n)
                else
                        ch = CHILD(n, i+1);
                REQ(ch, NAME);
-               c->c_flags |= CO_VARKEYWORDS;
-               com_newlocal(c, STR(ch));
        }
        if (complex) {
                /* Generate code for complex arguments only after
@@ -3395,7 +3553,7 @@ com_file_input(struct compiling *c, node *n)
                Py_DECREF(doc);
                com_addoparg(c, LOAD_CONST, i);
                com_push(c, 1);
-               com_addopnamestr(c, STORE_NAME, "__doc__");
+               com_addop_name(c, STORE_NAME, "__doc__");
                com_pop(c, 1);
        }
        for (i = 0; i < NCH(n); i++) {
@@ -3462,9 +3620,7 @@ compile_classdef(struct compiling *c, node *n)
        REQ(n, classdef);
        /* classdef: 'class' NAME ['(' testlist ')'] ':' suite */
        c->c_name = STR(CHILD(n, 1));
-#ifdef PRIVATE_NAME_MANGLING
        c->c_private = c->c_name;
-#endif
        ch = CHILD(n, NCH(n)-1); /* The suite */
        doc = get_docstring(ch);
        if (doc != NULL) {
@@ -3472,7 +3628,7 @@ compile_classdef(struct compiling *c, node *n)
                Py_DECREF(doc);
                com_addoparg(c, LOAD_CONST, i);
                com_push(c, 1);
-               com_addopnamestr(c, STORE_NAME, "__doc__");
+               com_addop_name(c, STORE_NAME, "__doc__");
                com_pop(c, 1);
        }
        else
@@ -3537,122 +3693,6 @@ compile_node(struct compiling *c, node *n)
        }
 }
 
-/* Optimization for local variables in functions (and *only* functions).
-
-   This replaces all LOAD_NAME, STORE_NAME and DELETE_NAME
-   instructions that refer to local variables with LOAD_FAST etc.
-   The latter instructions are much faster because they don't need to
-   look up the variable name in a dictionary.
-
-   To find all local variables, we check all STORE_NAME, IMPORT_FROM
-   and DELETE_NAME instructions.  This yields all local variables,
-   function definitions, class definitions and import statements.
-   Argument names have already been entered into the list by the
-   special processing for the argument list.
-
-   All remaining LOAD_NAME instructions must refer to non-local (global
-   or builtin) variables, so are replaced by LOAD_GLOBAL.
-
-   There are two problems:  'from foo import *' and 'exec' may introduce
-   local variables that we can't know while compiling.  If this is the
-   case, we can still optimize bona fide locals (since those
-   statements will be surrounded by fast_2_locals() and
-   locals_2_fast()), but we can't change LOAD_NAME to LOAD_GLOBAL.
-
-   NB: this modifies the string object c->c_code!  */
-
-static void
-optimize(struct compiling *c)
-{
-       unsigned char *next_instr, *cur_instr;
-       int opcode;
-       int oparg = 0;
-       PyObject *name;
-       PyObject *error_type, *error_value, *error_traceback;
-       
-#define NEXTOP()       (*next_instr++)
-#define NEXTARG()      (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2])
-#define GETITEM(v, i)  (PyList_GetItem((v), (i)))
-#define GETNAMEOBJ(i)  (GETITEM(c->c_names, (i)))
-       
-       PyErr_Fetch(&error_type, &error_value, &error_traceback);
-
-       c->c_flags |= CO_OPTIMIZED;
-       
-       next_instr = (unsigned char *) PyString_AsString(c->c_code);
-       for (;;) {
-               opcode = NEXTOP();
-               if (opcode == STOP_CODE)
-                       break;
-               if (HAS_ARG(opcode))
-                       oparg = NEXTARG();
-         dispatch_opcode1:
-               switch (opcode) {
-               case STORE_NAME:
-               case DELETE_NAME:
-               case IMPORT_FROM:
-                       com_addlocal_o(c, GETNAMEOBJ(oparg));
-                       break;
-               case IMPORT_STAR:
-               case EXEC_STMT:
-                       c->c_flags &= ~CO_OPTIMIZED;
-                       break;
-               case EXTENDED_ARG:
-                       opcode = NEXTOP();
-                       oparg = oparg<<16 | NEXTARG();
-                       goto dispatch_opcode1;
-                       break;
-               }
-       }
-       
-       /* TBD: Is this still necessary ? */
-       if (PyDict_GetItemString(c->c_locals, "*") != NULL)
-               c->c_flags &= ~CO_OPTIMIZED;
-       
-       next_instr = (unsigned char *) PyString_AsString(c->c_code);
-       for (;;) {
-               cur_instr = next_instr;
-               opcode = NEXTOP();
-               if (opcode == STOP_CODE)
-                       break;
-               if (HAS_ARG(opcode))
-                       oparg = NEXTARG();
-         dispatch_opcode2:
-               if (opcode == LOAD_NAME ||
-                   opcode == STORE_NAME ||
-                   opcode == DELETE_NAME) {
-                       PyObject *v;
-                       int i;
-                       name = GETNAMEOBJ(oparg);
-                       v = PyDict_GetItem(c->c_locals, name);
-                       if (v == NULL) {
-                               if (opcode == LOAD_NAME &&
-                                   (c->c_flags&CO_OPTIMIZED))
-                                       cur_instr[0] = LOAD_GLOBAL;
-                               continue;
-                       }
-                       i = PyInt_AsLong(v);
-                       if (i >> 16) /* too big for 2 bytes */
-                               continue;
-                       switch (opcode) {
-                       case LOAD_NAME: cur_instr[0] = LOAD_FAST; break;
-                       case STORE_NAME: cur_instr[0] = STORE_FAST; break;
-                       case DELETE_NAME: cur_instr[0] = DELETE_FAST; break;
-                       }
-                       cur_instr[1] = i & 0xff;
-                       cur_instr[2] = i >> 8;
-               }
-               if (opcode == EXTENDED_ARG) {
-                       opcode = NEXTOP();
-                       oparg = oparg<<16 | NEXTARG();
-                       goto dispatch_opcode2;
-               }
-       }
-
-       if (c->c_errors == 0)
-               PyErr_Restore(error_type, error_value, error_traceback);
-}
-
 PyCodeObject *
 PyNode_Compile(node *n, char *filename)
 {
@@ -3672,21 +3712,21 @@ jcompile(node *n, char *filename, struct compiling *base)
        PyCodeObject *co;
        if (!com_init(&sc, filename))
                return NULL;
-#ifdef PRIVATE_NAME_MANGLING
-       if (base)
+       if (base) {
                sc.c_private = base->c_private;
-       else
+               sc.c_symtable = base->c_symtable;
+       } else {
                sc.c_private = NULL;
-#endif
-       compile_node(&sc, n);
-       com_done(&sc);
-       if ((TYPE(n) == funcdef || TYPE(n) == lambdef) && sc.c_errors == 0) {
-               optimize(&sc);
-               sc.c_flags |= CO_NEWLOCALS;
+               if (symtable_build(&sc, n) < 0) {
+                       com_free(&sc);
+                       return NULL;
+               }
        }
-       else if (TYPE(n) == classdef)
-               sc.c_flags |= CO_NEWLOCALS;
        co = NULL;
+       if (symtable_load_symbols(&sc) < 0)
+               goto exit;
+       compile_node(&sc, n);
+       com_done(&sc);
        if (sc.c_errors == 0) {
                PyObject *consts, *names, *varnames, *filename, *name;
                consts = PyList_AsTuple(sc.c_consts);
@@ -3721,6 +3761,9 @@ jcompile(node *n, char *filename, struct compiling *base)
                   it gets reported instead dumping core. */
                PyErr_SetString(PyExc_SystemError, "lost syntax error");
        }
+ exit:
+       if (base == NULL)
+               symtable_free(sc.c_symtable);
        com_free(&sc);
        return co;
 }
@@ -3740,3 +3783,712 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq)
        }
        return line;
 }
+
+static int
+is_local(struct compiling *c, char *name)
+{
+       if (PyDict_GetItemString(c->c_locals, name) != NULL)
+               return 1;
+       else
+               return 0;
+}
+
+static int
+is_global(struct compiling *c, char *name)
+{
+       PyObject *v;
+       v = PyDict_GetItemString(c->c_globals, name);
+       if (v == NULL)
+               return 0;
+       else if (v == Py_None)
+               return IMPLICIT_GLOBAL;
+       else
+               return EXPLICIT_GLOBAL;
+}
+
+static int
+symtable_build(struct compiling *c, node *n)
+{
+       if ((c->c_symtable = symtable_init()) == NULL)
+               return -1;
+       if (symtable_enter_scope(c->c_symtable, GLOBAL, TYPE(n)) < 0)
+               return -1;
+       symtable_node(c->c_symtable, n);
+       /* reset for second pass */
+       c->c_symtable->st_scopes = 1;
+       return 0;
+}
+
+static int
+symtable_load_symbols(struct compiling *c)
+{
+       static PyObject *global = NULL;
+       PyObject *name, *v, *varnames;
+       int i, info, count, pos;
+       struct symtable *st = c->c_symtable;
+
+       varnames = PyDict_GetItem(st->st_varnames, st->st_cur_id);
+       if (varnames == NULL) {
+               varnames = PyList_New(0);
+               if (varnames == NULL)
+                       return -1;
+       } else
+               Py_INCREF(varnames);
+
+       c->c_varnames = varnames;
+       count = PyList_GET_SIZE(varnames);
+       c->c_argcount = count;
+       for (i = 0; i < count; ++i) {
+               v = PyInt_FromLong(i);
+               if (PyDict_SetItem(c->c_locals, 
+                                  PyList_GET_ITEM(varnames, i), v) < 0)
+                       return -1;
+       }
+
+       /* XXX The cases below define the rules for whether a name is
+          local or global.  The logic could probably be clearer. */
+       pos = 0;
+       while (PyDict_Next(st->st_cur, &pos, &name, &v)) {
+               info = PyInt_AS_LONG(v);
+               if (info & DEF_STAR) {
+                       c->c_argcount--;
+                       c->c_flags |= CO_VARARGS;
+               } else if (info & DEF_DOUBLESTAR) {
+                       c->c_argcount--;
+                       c->c_flags |= CO_VARKEYWORDS;
+               } else if (info & DEF_INTUPLE) 
+                       c->c_argcount--;
+               else if (info & DEF_GLOBAL) {
+                       if ((info & DEF_PARAM) 
+                           && (PyString_AS_STRING(name)[0] != '.')){
+                               char buf[500];
+                               sprintf(buf, 
+                                       "name '%.400s' is local and global",
+                                       PyString_AS_STRING(name));
+                               com_error(c, PyExc_SyntaxError, buf);
+                               return -1;
+                       }
+                       if (global == NULL) {
+                               global = PyInt_FromLong(1);
+                               if (global == NULL) {
+                                       return -1;
+                               }
+                       }
+                       if (PyDict_SetItem(c->c_globals, name, global) < 0)
+                               return -1;
+               } else if ((info & USE) && !(info & (DEF_LOCAL | DEF_PARAM))) {
+                       if (PyDict_SetItem(c->c_globals, name,
+                                          Py_None) < 0)
+                               return -1;
+               } else if ((info & DEF_LOCAL) && !(info & DEF_PARAM)) {
+                       v = PyInt_FromLong(count++);
+                       if (PyDict_SetItem(c->c_locals, name, v) < 0)
+                               return -1;
+                       if (st->st_cur_type != TYPE_CLASS) 
+                               if (PyList_Append(c->c_varnames, name) < 0)
+                                       return -1;
+               }
+       }
+
+       if (st->st_cur_type == TYPE_FUNCTION)
+               c->c_nlocals = count;
+
+       if (st->st_cur_type != TYPE_MODULE)
+               c->c_flags |= CO_NEWLOCALS;
+       if ((st->st_cur_type == TYPE_FUNCTION)
+           && (PyDict_GetItemString(st->st_cur, NOOPT) == NULL))
+               c->c_flags |= CO_OPTIMIZED;
+
+       return 0;
+}
+
+static struct symtable *
+symtable_init()
+{
+       struct symtable *st;
+       PyObject *d;
+
+       st = (struct symtable *)PyMem_Malloc(sizeof(struct symtable));
+       if (st == NULL)
+               return NULL;
+       if ((st->st_namespaces = PyList_New(0)) == NULL)
+               goto fail;
+       if ((st->st_types = PyList_New(0)) == NULL)
+               goto fail;
+       if ((st->st_symbols = PyDict_New()) == NULL)
+               goto fail; 
+       if ((st->st_varnames = PyDict_New()) == NULL)
+               goto fail; 
+       if ((d = PyDict_New()) == NULL) 
+               goto fail;
+       if (PyDict_SetItemString(st->st_symbols, GLOBAL, d) < 0)
+               goto fail;
+       st->st_global = d;
+       st->st_cur = NULL;
+       st->st_cur_id = NULL;
+       st->st_cur_type = 0;
+       st->st_scopes = 0;
+       st->st_errors = 0;
+       st->st_tmpname = 0;
+       st->st_private = NULL;
+       return st;
+ fail:
+       symtable_free(st);
+       return NULL;
+}
+
+static void
+symtable_free(struct symtable *st)
+{
+       Py_XDECREF(st->st_symbols);
+       Py_XDECREF(st->st_varnames);
+       Py_XDECREF(st->st_namespaces);
+       Py_XDECREF(st->st_types);
+       Py_XDECREF(st->st_cur_id);
+       PyMem_Free((void *)st);
+}
+
+/* XXX name isn't used ... */
+
+static int
+symtable_enter_scope(struct symtable *st, char *name, int type)
+{
+       PyObject *o;
+       
+       o = PyInt_FromLong(st->st_scopes++);
+       if (o == NULL)
+               return -1;
+       if (PyList_Append(st->st_namespaces, o) < 0)
+               return -1;
+       switch (type) {
+       case funcdef:
+       case lambdef:
+               st->st_cur_type = TYPE_FUNCTION;
+               break;
+       case classdef:
+               st->st_cur_type = TYPE_CLASS;
+               break;
+       case single_input:
+       case eval_input:
+       case file_input:
+               st->st_cur_type = TYPE_MODULE;
+               break;
+       default:
+               fprintf(stderr, "invalid symtable scope: %d\n", type);
+       }
+       o = PyInt_FromLong(st->st_cur_type);
+       if (o == NULL)
+               return -1;
+       if (PyList_Append(st->st_types, o) < 0)
+               return -1;
+       return symtable_update_cur(st);
+}
+
+static int
+symtable_exit_scope(struct symtable *st)
+{
+       PyObject *o;
+       int end;
+
+       end = PyList_GET_SIZE(st->st_namespaces) - 1;
+       if (PySequence_DelItem(st->st_namespaces, end) < 0)
+               return -1;
+       if (PySequence_DelItem(st->st_types, end) < 0)
+               return -1;
+       o = PyList_GET_ITEM(st->st_types, end - 1);
+       st->st_cur_type = PyInt_AS_LONG(o);
+       return symtable_update_cur(st);
+}
+
+static int
+symtable_update_cur(struct symtable *st)
+{
+       PyObject *s, *d, *l;
+       int end;
+
+       end = PyList_GET_SIZE(st->st_namespaces) - 1;
+       s = PyList_GET_ITEM(st->st_namespaces, end);
+       st->st_cur_id = s;
+       d = PyDict_GetItem(st->st_symbols, s);
+       if (d == NULL) {
+               if ((d = PyDict_New()) == NULL)
+                       return -1;
+               if (PyObject_SetItem(st->st_symbols, s, d) < 0) {
+                       Py_DECREF(d);
+                       return -1;
+               }
+               if (st->st_cur_type == TYPE_FUNCTION) {
+                       if ((l = PyList_New(0)) == NULL)
+                               return -1;
+                       if (PyDict_SetItem(st->st_varnames, s, l) < 0)
+                               return -1;
+               }
+       }
+       
+       st->st_cur = d;
+       return 0;
+}
+
+static int
+symtable_mangle(struct symtable *st, char *name, char *buffer, size_t maxlen)
+{
+       return mangle(st->st_private, name, buffer, maxlen);
+}
+
+static int
+symtable_add_def(struct symtable *st, char *name, int scope)
+{
+       PyObject *s, *o;
+       int val;
+       char buffer[MANGLE_LEN];
+
+       if (symtable_mangle(st, name, buffer, sizeof(buffer)))
+               name = buffer;
+       if ((s = PyString_InternFromString(name)) == NULL)
+               return -1;
+       if ((o = PyDict_GetItem(st->st_cur, s))) {
+           val = PyInt_AS_LONG(o);
+           if ((scope & DEF_PARAM) && (val & DEF_PARAM)) {
+                   PyErr_Format(PyExc_SyntaxError,
+                        "duplicate argument '%s' in function definition",
+                                name);
+                   return -1;
+           }
+           val |= scope;
+       } else
+           val = scope;
+       o = PyInt_FromLong(val);
+       if (PyDict_SetItem(st->st_cur, s, o) < 0) {
+               Py_DECREF(o);
+               return -1;
+       }
+       Py_DECREF(o);
+
+       if (scope & DEF_PARAM) {
+               PyObject *l = PyDict_GetItem(st->st_varnames, 
+                                            st->st_cur_id);
+               if (l == NULL)
+                       return -1;
+               if (PyList_Append(l, s) < 0) 
+                       return -1;
+       } else  if (scope & DEF_GLOBAL) {
+               if ((o = PyDict_GetItem(st->st_global, s))) {
+                       val = PyInt_AS_LONG(o);
+                       val |= scope;
+               } else
+                       val = scope;
+               o = PyInt_FromLong(val);
+               if (PyDict_SetItem(st->st_global, s, o) < 0) {
+                       Py_DECREF(o);
+                       return -1;
+               }
+               Py_DECREF(o);
+       }
+       return 0;
+}
+
+static int
+symtable_add_use(struct symtable *st, char *name)
+{
+       PyObject *s, *o;
+       int val;
+       char buffer[MANGLE_LEN];
+
+       if (symtable_mangle(st, name, buffer, sizeof(buffer)))
+               name = buffer;
+/*     fprintf(stderr, "add_use(%s)\n", name); */
+       if ((s = PyString_InternFromString(name)) == NULL)
+               return -1;
+       if ((o = PyDict_GetItem(st->st_cur, s))) {
+               val = PyInt_AS_LONG(o);
+               val |= USE;
+       } else
+               val = USE;
+       o = PyInt_FromLong(val);
+       if (PyDict_SetItem(st->st_cur, s, o) < 0) {
+               Py_DECREF(o);
+               return -1;
+       }
+       Py_DECREF(o);
+       return 0;
+}
+
+static void
+symtable_node(struct symtable *st, node *n)
+{
+       int i, start = 0;
+
+ loop:
+       switch (TYPE(n)) {
+       case funcdef: {
+               char *func_name = STR(CHILD(n, 1));
+               symtable_add_def(st, func_name, DEF_LOCAL);
+               symtable_default_args(st, CHILD(n, 2));
+               symtable_enter_scope(st, func_name, TYPE(n));
+               symtable_funcdef(st, n);
+               symtable_exit_scope(st);
+               break;
+       }
+       case lambdef:
+               if (NCH(n) == 4)
+                       symtable_default_args(st, CHILD(n, 1));
+               symtable_enter_scope(st, "lambda", TYPE(n));
+               symtable_funcdef(st, n);
+               symtable_exit_scope(st);
+               break;
+       case classdef: {
+               char *tmp, *class_name = STR(CHILD(n, 1));
+               symtable_add_def(st, class_name, DEF_LOCAL);
+               if (TYPE(CHILD(n, 2)) == LPAR) {
+                       node *bases = CHILD(n, 3);
+                       int i;
+                       for (i = 0; i < NCH(bases); i += 2) {
+                               symtable_node(st, CHILD(bases, i));
+                       }
+               }
+               symtable_enter_scope(st, class_name, TYPE(n));
+               tmp = st->st_private;
+               st->st_private = class_name;
+               symtable_node(st, CHILD(n, NCH(n) - 1));
+               st->st_private = tmp;
+               symtable_exit_scope(st);
+               break;
+       }
+       case if_stmt:
+               for (i = 0; i + 3 < NCH(n); i += 4) {
+                       if (is_constant_false(NULL, (CHILD(n, i + 1))))
+                               continue;
+                       symtable_node(st, CHILD(n, i + 1));
+                       symtable_node(st, CHILD(n, i + 3));
+               }
+               if (i + 2 < NCH(n))
+                       symtable_node(st, CHILD(n, i + 2));
+               break;
+       case global_stmt:
+               symtable_global(st, n);
+               break;
+       case import_stmt:
+               symtable_import(st, n);
+               break;
+       case exec_stmt:
+               if (PyDict_SetItemString(st->st_cur, NOOPT, Py_None) < 0) 
+                       st->st_errors++;
+               symtable_node(st, CHILD(n, 1));
+               if (NCH(n) > 2)
+                       symtable_node(st, CHILD(n, 3));
+               if (NCH(n) > 4)
+                       symtable_node(st, CHILD(n, 5));
+               break;
+       case except_clause:
+               if (NCH(n) == 4)
+                       symtable_assign(st, CHILD(n, 3));
+               if (NCH(n) > 1) {
+                       n = CHILD(n, 1);
+                       goto loop;
+               }
+               break;
+       case del_stmt:
+               symtable_assign(st, CHILD(n, 1));
+               break;
+       case expr_stmt:
+               if (NCH(n) == 1)
+                       n = CHILD(n, 0);
+               else {
+                       if (TYPE(CHILD(n, 1)) == augassign) {
+                               symtable_assign(st, CHILD(n, 0));
+                               symtable_node(st, CHILD(n, 2));
+                               break;
+                       } else {
+                               int i;
+                               for (i = 0; i < NCH(n) - 2; i += 2) 
+                                       symtable_assign(st, CHILD(n, i));
+                               n = CHILD(n, NCH(n) - 1);
+                       }
+               }
+               goto loop;
+               /* watchout for fall-through logic below */
+       case listmaker:
+               if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) {
+                       symtable_list_comprehension(st, CHILD(n, 1));
+                       break;
+               }
+       case atom:
+               if (TYPE(n) == atom && TYPE(CHILD(n, 0)) == NAME) {
+                       symtable_add_use(st, STR(CHILD(n, 0)));
+                       break;
+               }
+       case for_stmt:
+               if (TYPE(n) == for_stmt) {
+                       symtable_assign(st, CHILD(n, 1));
+                       start = 3;
+               }
+       default:
+               if (NCH(n) == 1) {
+                       n = CHILD(n, 0);
+                       goto loop;
+               }
+               for (i = start; i < NCH(n); ++i)
+                       if (TYPE(CHILD(n, i)) >= single_input)
+                               symtable_node(st, CHILD(n, i));
+       }
+}
+
+static void
+symtable_funcdef(struct symtable *st, node *n)
+{
+       node *body;
+
+       if (TYPE(n) == lambdef) {
+               if (NCH(n) == 4)
+                       symtable_params(st, CHILD(n, 1));
+       } else
+               symtable_params(st, CHILD(n, 2));
+       body = CHILD(n, NCH(n) - 1);
+       symtable_node(st, body);
+}
+
+/* The next two functions parse the argument tuple.
+   symtable_default_arg() checks for names in the default arguments,
+   which are references in the defining scope.  symtable_params()
+   parses the parameter names, which are defined in the function's
+   body. 
+
+   varargslist: 
+       (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) 
+       | fpdef ['=' test] (',' fpdef ['=' test])* [',']
+*/
+
+static void
+symtable_default_args(struct symtable *st, node *n)
+{
+       node *c;
+       int i;
+
+       if (TYPE(n) == parameters) {
+               n = CHILD(n, 1);
+               if (TYPE(n) == RPAR)
+                       return;
+       }
+       REQ(n, varargslist);
+       for (i = 0; i < NCH(n); i += 2) {
+               c = CHILD(n, i);
+               if (TYPE(c) == STAR || TYPE(c) == DOUBLESTAR) {
+                       break;
+               }
+               if (i > 0 && (TYPE(CHILD(n, i - 1)) == EQUAL))
+                       symtable_node(st, CHILD(n, i));
+       }
+}
+
+static void
+symtable_params(struct symtable *st, node *n)
+{
+       int i, complex = 0, ext = 0;
+       node *c = NULL;
+
+       if (TYPE(n) == parameters) {
+               n = CHILD(n, 1);
+               if (TYPE(n) == RPAR)
+                       return;
+       }
+       REQ(n, varargslist);
+       for (i = 0; i < NCH(n); i += 2) {
+               c = CHILD(n, i);
+               if (TYPE(c) == STAR || TYPE(c) == DOUBLESTAR) {
+                       ext = 1;
+                       break;
+               }
+               if (TYPE(c) == test) {
+                       continue;
+               }
+               if (TYPE(CHILD(c, 0)) == NAME)
+                       symtable_add_def(st, STR(CHILD(c, 0)), DEF_PARAM);
+               else {
+                       char nbuf[10];
+                       sprintf(nbuf, ".%d", i);
+                       symtable_add_def(st, nbuf, DEF_PARAM);
+                       complex = 1;
+               }
+       }
+       if (complex) {
+               int j;
+               for (j = 0; j < i; j += 2) {
+                       c = CHILD(n, j);
+                       if (TYPE(CHILD(c, 0)) == LPAR)
+                               symtable_params_fplist(st, CHILD(c, 1));
+               } 
+       }
+       if (ext) {
+               c = CHILD(n, i);
+               if (TYPE(c) == STAR) {
+                       i++;
+                       symtable_add_def(st, STR(CHILD(n, i)), 
+                                        DEF_PARAM | DEF_STAR);
+                       i += 2;
+                       c = CHILD(n, i);
+               }
+               if (TYPE(c) == DOUBLESTAR) {
+                       i++;
+                       symtable_add_def(st, STR(CHILD(n, i)), 
+                                        DEF_PARAM | DEF_DOUBLESTAR);
+               }
+       }
+}
+
+static void
+symtable_params_fplist(struct symtable *st, node *n)
+{
+       int i;
+       node *c;
+
+       REQ(n, fplist);
+       for (i = 0; i < NCH(n); i += 2) {
+               c = CHILD(n, i);
+               REQ(c, fpdef);
+               if (NCH(c) == 1)
+                       symtable_add_def(st, STR(CHILD(c, 0)), 
+                                        DEF_PARAM | DEF_INTUPLE);
+               else
+                       symtable_params_fplist(st, CHILD(c, 1));
+       }
+       
+}
+
+static void
+symtable_global(struct symtable *st, node *n)
+{
+       int i;
+
+       for (i = 1; i < NCH(n); i += 2)
+               symtable_add_def(st, STR(CHILD(n, i)), DEF_GLOBAL);
+}
+
+static void
+symtable_list_comprehension(struct symtable *st, node *n)
+{
+       char tmpname[12];
+
+       sprintf(tmpname, "[%d]", ++st->st_tmpname);
+       symtable_add_def(st, tmpname, DEF_LOCAL);
+       symtable_assign(st, CHILD(n, 1));
+       symtable_node(st, CHILD(n, 3));
+       if (NCH(n) == 5)
+               symtable_node(st, CHILD(n, 4));
+       --st->st_tmpname;
+}
+
+static void
+symtable_import(struct symtable *st, node *n)
+{
+       int i;
+       /*
+         import_stmt: 'import' dotted_as_name (',' dotted_as_name)* 
+              | 'from' dotted_name 'import' 
+                                ('*' | import_as_name (',' import_as_name)*)
+         import_as_name: NAME [NAME NAME]
+       */
+
+       if (STR(CHILD(n, 0))[0] == 'f') {  /* from */
+               if (TYPE(CHILD(n, 3)) == STAR) {
+                       if (PyDict_SetItemString(st->st_cur, NOOPT,
+                                                Py_None) < 0)
+                               st->st_errors++;
+               } else {
+                       for (i = 3; i < NCH(n); i += 2) {
+                               node *c = CHILD(n, i);
+                               if (NCH(c) > 1) /* import as */
+                                       symtable_assign(st, CHILD(c, 2));
+                               else
+                                       symtable_assign(st, CHILD(c, 0));
+                       }
+               }
+       } else {
+               for (i = 1; i < NCH(n); i += 2) {
+                       symtable_assign(st, CHILD(n, i));
+               }
+       }
+}
+
+static void 
+symtable_assign(struct symtable *st, node *n)
+{
+       node *tmp;
+       int i;
+
+       for (;;) {
+/*             fprintf(stderr, "symtable_assign(%d, %d)\n",
+                       TYPE(n), NCH(n));
+*/
+
+               switch (TYPE(n)) {
+               case power:
+                       /* not sure that NCH(n) > 1 always means that
+                          none of the left-hand side names are
+                          targets of assignments */
+                       if (NCH(n) > 2) {
+                               for (i = 2; i < NCH(n); ++i)
+                                       if (TYPE(CHILD(n, i)) != DOUBLESTAR)
+                                               symtable_node(st, CHILD(n, i));
+                       }
+                       if (NCH(n) > 1) { 
+                               symtable_node(st, CHILD(n, 0));
+                               symtable_node(st, CHILD(n, 1));
+                       } else {
+                               n = CHILD(n, 0);
+                               continue;
+                       }
+                       return;
+               case listmaker:
+                       if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for)
+                               symtable_list_comprehension(st, CHILD(n, 1));
+                       else {
+                               for (i = 0; i < NCH(n); i += 2)
+                                       symtable_assign(st, CHILD(n, i));
+                       }
+                       return;
+               case exprlist:
+               case testlist:
+                       if (NCH(n) == 1) {
+                               n = CHILD(n, 0);
+                               break;
+                       }
+                       else {
+                               int i;
+                               for (i = 0; i < NCH(n); i += 2)
+                                       symtable_assign(st, CHILD(n, i));
+                               return;
+                       }
+                       break;
+               case atom:
+                       tmp = CHILD(n, 0);
+                       if (TYPE(tmp) == LPAR || TYPE(tmp) == LSQB) {
+                               n = CHILD(n, 1);
+                               continue;
+                       } else if (TYPE(tmp) == NAME)
+                               symtable_add_def(st, STR(tmp), DEF_LOCAL);
+                       return;
+               case dotted_as_name:
+                       if (NCH(n) == 3)
+                               symtable_add_def(st, STR(CHILD(n, 2)),
+                                                DEF_LOCAL);
+                       else
+                               symtable_add_def(st,
+                                                STR(CHILD(CHILD(n,
+                                                                0), 0)),
+                                                DEF_LOCAL);
+                       return;
+               case dotted_name:
+                       symtable_add_def(st, STR(CHILD(n, 0)), DEF_LOCAL);
+                       return;
+               case NAME:
+                       symtable_add_def(st, STR(n), DEF_LOCAL);
+                       return;
+               default:
+                       if (NCH(n) == 0)
+                               return;
+                       assert(NCH(n) == 1);
+                       n = CHILD(n, 0);
+                       break;
+               }
+       }
+}